Related
I recently found how to add an event when changing a property of an Item in my ListView :
foreach (Profil item in gp.ListProfils)
{
if (item is INotifyPropertyChanged)
{
INotifyPropertyChanged observable = (INotifyPropertyChanged)item;
observable.PropertyChanged +=
new PropertyChangedEventHandler(ItemPropertyChanged);
}
}
Now, I would like to apply the modify to all selected items.
I found how to identify the name of property changed :
private void ItemPropertyChanged(object sender, PropertyChangedEventArgs e)
{
if(sender is Profil)
{
string myPropertyName = e.PropertyName;
Profil editedProfile = (Profil)sender;
foreach(Profil prf in this.SelectedProfiles)
{
//prf.NAME_PROPERTY = editedProfile.NAME_PROPERTY
if (!this.ListProfilToEdit.Contains(editedProfile))
{
this.ListProfilToEdit.Add(editedProfile);
}
}
}
}
But how can I replace the line in comment.
Concretely if I edited the field "Width", I want to change the width for all selected elements.
Is there a way to do it, without creating a separated function EditProperty(string nameProperty)?
Here you need to be able to read a property dynamically from an object and then write it dynamically on another object.
You can use Expression Trees to generate Getters & Setters dynamically and them put in the cache.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
namespace CoreAppMain
{
public class TypeAccessors<T> where T : class
{
//Getters
public static Dictionary<string, Func<T, object>> Getters = GetTypeGetters();
private static Dictionary<string, Func<T, object>> GetTypeGetters()
{
var _getters = new Dictionary<string, Func<T, object>>();
var props = typeof(T).GetProperties();
foreach (var p in props)
{
var entityParam = Expression.Parameter(typeof(T), "e");
Expression columnExpr = Expression.Property(entityParam, p);
Expression conversion = Expression.Convert(columnExpr, typeof(object));
var fct = Expression.Lambda<Func<T, object>>(conversion, entityParam).Compile();
_getters.Add(p.Name, fct);
}
return _getters;
}
//setters
public static Dictionary<string, Action<T, object>> Setters = GetTypeSetters();
public static Dictionary<string, Action<T, object>> GetTypeSetters()
{
var setters = new Dictionary<string, Action<T, object>>();
const BindingFlags flags = BindingFlags.Public | BindingFlags.Instance;
var propsToSet = typeof(T).GetProperties(flags)
.Where(x => x.CanWrite)
.ToList();
foreach (var field in propsToSet)
{
var propertyInfo = typeof(T).GetProperty(field.Name);
setters.Add(field.Name, GetPropertySetter<T>(propertyInfo));
}
return setters;
}
public static Action<TModel, object> GetPropertySetter<TModel>(PropertyInfo propertyInfo)
{
// Note that we are testing whether this is a value type
bool isValueType = propertyInfo.DeclaringType.IsValueType;
var method = propertyInfo.GetSetMethod(true);
var obj = Expression.Parameter(typeof(object), "o");
var value = Expression.Parameter(typeof(object));
// Note that we are using Expression.Unbox for value types
// and Expression.Convert for reference types
Expression<Action<TModel, object>> expr =
Expression.Lambda<Action<TModel, object>>(
Expression.Call(
isValueType ?
Expression.Unbox(obj, method.DeclaringType) :
Expression.Convert(obj, method.DeclaringType),
method,
Expression.Convert(value, method.GetParameters()[0].ParameterType)),
obj, value);
Action<TModel, object> action = expr.Compile();
return action;
}
}
}
Usage Example:
var cust1 = new Customer()
{
FirstName = "TOTOT",
LastName = "TITI"
};
var cust2 = new Customer()
{
FirstName = "XXXXXX",
LastName = "YYYYYY"
};
var value = TypeAccessors<Customer>.Getters[nameof(Customer.FirstName)](cust2);
TypeAccessors<Customer>.Setters[nameof(Customer.FirstName)](cust1, value);
Console.WriteLine(cust1.FirstName);
Console.ReadKey();
Is there a way to do it, without creating a separated function EditProperty(string nameProperty)?
Creating a separate method or not is a matter of good design and clean code, it does not affect the core of the question in any way. You can always create a separate method or write the code in your existing method.
But how can I replace the line in comment.
It is not exactly clear what you are doing, so I am in doubt whether what you are asking could be solved in different way, but as it is, you are asking to read and write properties of instances by only knowing their names. You will need to use Reflection for this.
For performing late binding, accessing methods on types created at run time. See the topic Dynamically Loading and Using Types.
Assuming your properties are public, this should work:
var propertyInfo = typeof(Profil).GetProperty(myPropertyName);
var value = propertyInfo.GetValue(editedProfile, null);
foreach(Profil prf in this.SelectedProfiles)
{
propertyInfo.SetValue(prf, value);
// ...other code.
}
That said, be careful using reflection, for further reading:
Reflection: Is using reflection still “bad” or “slow”?
If reflection is inefficient, when is it most appropriate?
I'm comparing two List<Dictionary<string, object>> with my own IEqualityComparer<Dictionary<string, object>> implementation, but neither GetHashCode nor Equals method get called.
Here's my own IEqualityComparer implementation.
public class TestEqualityComparer : IEqualityComparer<Dictionary<string, object>>
{
public bool Equals(Dictionary<string, object> a, Dictionary<string, object> b)
{
return true; // breakpoint here
}
public int GetHashCode(Dictionary<string, object> obj)
{
return 0; // breakpoint here
}
}
And here's a actual comparing code.
var a = new List<Dictionary<string, object>>();
var b = new List<Dictionary<string, object>>();
a.Add(new Dictionary<string, object> { ["id"] = 1, ["msg"] = "aaaaa" });
a.Add(new Dictionary<string, object> { ["id"] = 2, ["msg"] = "bbbbb" });
a.Add(new Dictionary<string, object> { ["id"] = 3, ["msg"] = "ccccc" });
b.Add(new Dictionary<string, object> { ["id"] = 1, ["msg"] = "zzzzz" });
b.Add(new Dictionary<string, object> { ["id"] = 2, ["msg"] = "bbbbb" });
b.Add(new Dictionary<string, object> { ["id"] = 4, ["msg"] = "ddddd" });
var except = a.Except(b, new TestEqualityComparer());
When I ran the above code, breakpoints never got triggered.
What's the problem?
Since LINQ uses deferred execution the contents of except collection will not be determined unless you decide to iterate over it, hence no calls to your IEqualityComparer.
To force evaluation of your Except statement you can either iterate over it with foreach or append ToList/ToArray to your statement, like so:
var except = a.Except(b, new TestEqualityComparer()).ToList(); // ToList forces processing of LINQ query
I have a List<CustomObject>
which has 3 properties
A,B,C
and what I need is to transform this List to a Dictionary so the result looks like
Dictionary<string,object>
(Property name) A = Value of A
(Property name) B = Value of B
(Property name) C = Value of C
Pls suggest...
CustomObject instance = new CustomObject();
var dict = instance.GetType().GetProperties()
.ToDictionary(p => p.Name, p => p.GetValue(instance, null));
I found the code :) Originally from here.
static T CreateDelegate<T>(this DynamicMethod dm) where T : class
{
return dm.CreateDelegate(typeof(T)) as T;
}
static Dictionary<Type, Func<object, Dictionary<string, object>>> cache =
new Dictionary<Type, Func<object, Dictionary<string, object>>>();
static Dictionary<string, object> GetProperties(object o)
{
var t = o.GetType();
Func<object, Dictionary<string, object>> getter;
if (!cache.TryGetValue(t, out getter))
{
var rettype = typeof(Dictionary<string, object>);
var dm = new DynamicMethod(t.Name + ":GetProperties", rettype,
new Type[] { typeof(object) }, t);
var ilgen = dm.GetILGenerator();
var instance = ilgen.DeclareLocal(t);
var dict = ilgen.DeclareLocal(rettype);
ilgen.Emit(OpCodes.Ldarg_0);
ilgen.Emit(OpCodes.Castclass, t);
ilgen.Emit(OpCodes.Stloc, instance);
ilgen.Emit(OpCodes.Newobj, rettype.GetConstructor(Type.EmptyTypes));
ilgen.Emit(OpCodes.Stloc, dict);
var add = rettype.GetMethod("Add");
foreach (var prop in t.GetProperties(
BindingFlags.Instance |
BindingFlags.Public))
{
ilgen.Emit(OpCodes.Ldloc, dict);
ilgen.Emit(OpCodes.Ldstr, prop.Name);
ilgen.Emit(OpCodes.Ldloc, instance);
ilgen.Emit(OpCodes.Ldfld, prop);
ilgen.Emit(OpCodes.Castclass, typeof(object));
ilgen.Emit(OpCodes.Callvirt, add);
}
ilgen.Emit(OpCodes.Ldloc, dict);
ilgen.Emit(OpCodes.Ret);
cache[t] = getter =
dm.CreateDelegate<Func<object, Dictionary<string, object>>>();
}
return getter(o);
}
For given type:
class Foo
{
public string A {get;}
public int B {get;}
public bool C {get;}
}
It produces a delegate equivalent to:
(Foo f) => new Dictionary<string, object>
{
{ "A", f.A },
{ "B", f.B },
{ "C", f.C },
};
Disclaimer: Looking at the code now (without testing) there may need to be special handling for valuetypes (instead of just the castclass). Exercise for the reader.
If I understand correctly what you want to do, you have to use reflrecion on CustomObject to get the property names then simply create the dictionary:
dic.add(propertyName, value);
If 'value' is an incoming generic dictionary whose types are unknown/don't matter, how do I take its entries and put them into a target dictionary of type IDictionary<object, object> ?
if(type == typeof(IDictionary<,>))
{
// this doesn't compile
// value is passed into the method as object and must be cast
IDictionary<,> sourceDictionary = (IDictionary<,>)value;
IDictionary<object,object> targetDictionary = new Dictionary<object,object>();
// this doesn't compile
foreach (KeyValuePair<,> sourcePair in sourceDictionary)
{
targetDictionary.Insert(sourcePair.Key, sourcePair.Value);
}
return targetDictionary;
}
EDIT:
Thanks for the responses so far.
The problem here is that the argument to Copy is only known as type 'object'. For example:
public void CopyCaller(object obj)
{
if(obj.GetType() == typeof(IDictionary<,>)
Copy(dictObj); // this doesn't compile
}
Make your method generic as well and then you'll be able to do what you're doing. You won't have to change your usage pattern since the compiler will be able to infer generic types from input types.
public IDictionary<object, object> Copy(IDictionary<TKey, TValue> source)
{
IDictionary<object,object> targetDictionary = new Dictionary<object,object>();
foreach (KeyValuePair<TKey, TValue> sourcePair in sourceDictionary)
{
targetDictionary.Insert(sourcePair.Key, sourcePair.Value);
}
return targetDictionary;
}
If you don't really need to convert it from IDictionary<TKey, TValue> to IDictionary<object, object> then you can use the copy constuctor of Dictionary<TKey, TValue> which accepts another dictionary as input and copies all values--just like you're doing now.
You can exploit the fact that generic dictionaries implement the IDictionary interface.
public static Dictionary<object, object> CreateCopy(IDictionary source)
{
var copy = new Dictionary<object, object>(source.Count);
foreach (DictionaryEntry entry in source)
{
copy.Add(entry.Key, entry.Value);
}
return copy;
}
Usage example:
var source = new Dictionary<int, string>() { { 1, "Foo" }, { 2, "Bar" }, };
var copy = CreateCopy(source);
Console.WriteLine(String.Join(", ", copy.Values));
Output:
Foo, Bar
Here is a method (don't leave it as static, unless you need it to be, I wrote it in a quick console app) that basically converts a Dictionary of any type to an object/object dictionary.
private static Dictionary<object,object> DeTypeDictionary<T,U>(Dictionary<T,U> inputDictionary)
{
Dictionary<object, object> returnDictionary = new Dictionary<object, object>();
foreach(T key in inputDictionary.Keys)
{
if( (key is object) && (inputDictionary[key] is object))
{
returnDictionary.Add(key, inputDictionary[key]);
}
else
{
//sorry these aren't objects. they may be dynamics.
continue;
}
}
return returnDictionary;
}
...and here is how you use it...
Dictionary<string, DateTime> d = new Dictionary<string, DateTime>();
d.Add("rsgfdg", DateTime.Now);
d.Add("gfdsgd", DateTime.Now);
Dictionary<object, object> newDictionary = DeTypeDictionary<string, DateTime>(d);
So you have an object that may be a Dictionary and you want to:
Test it's a dictionary
Act on it appropriately if it is
Let's start with a generic function that does what you want if you knew the type arguments:
class Bar
{
public static void Foo<TKey, TValue>(Dictionary<TKey, TValue> input) { ... }
}
Now we'll just have to do some reflection
bool TryHandleDictionary(object o)
{
var t = o.GetType();
if (!t.IsGenericType || t.GetGenericTypeDefinition() != typeof(Dictionary<,>)) return false;
var m = typeof(Bar).GetMethod("Foo", BindingFlags.Public | BindingFlags.Static);
var m1 = m.MakeGenericMethod(t.GetGenericArguments());
m1.Invoke(null, new[] { o });
return true;
}
This may be a fix for you but you'll need .net 3.5 or greater to use the var keyword.
// this should compile
foreach (var sourcePair in sourceDictionary)
{
targetDictionary.Insert(sourcePair.Key, sourcePair.Value);
}
I'm looking for a way to have a function such as:
myFunction({"Key", value}, {"Key2", value});
I'm sure there's something with anonymous types that would be pretty easy, but I'm not seeing it.
The only solution I can think of is to have a params KeyValuePair<String, object>[] pairs parameter, but that ends up being something similar to:
myFunction(new KeyValuePair<String, object>("Key", value),
new KeyValuePair<String, object>("Key2", value));
Which is, admittedly, much uglier.
EDIT:
To clarify, I'm writing a Message class to pass between 2 different systems. It contains a ushort specifying the the Message Type, and a dictionary of string to object for "Data" associated with the message. I'd like to be able to pass all this information in the constructor, so I am able to do this:
Agent.SendMessage(new Message(MessageTypes.SomethingHappened, "A", x, "B", y, "C", z));
or similar syntax.
When the syntax is bad for an otherwise decent pattern, change the syntax. How about:
public void MyFunction(params KeyValuePair<string, object>[] pairs)
{
// ...
}
public static class Pairing
{
public static KeyValuePair<string, object> Of(string key, object value)
{
return new KeyValuePair<string, object>(key, value);
}
}
Usage:
MyFunction(Pairing.Of("Key1", 5), Pairing.Of("Key2", someObject));
Even more interesting would be to add an extension method to string to make it pairable:
public static KeyValuePair<string, object> PairedWith(this string key, object value)
{
return new KeyValuePair<string, object>(key, value);
}
Usage:
MyFunction("Key1".PairedWith(5), "Key2".PairedWith(someObject));
Edit: You can also use the dictionary syntax without the generic brackets by deriving from Dictionary<,>:
public void MyFunction(MessageArgs args)
{
// ...
}
public class MessageArgs : Dictionary<string, object>
{}
Usage:
MyFunction(new MessageArgs { { "Key1", 5 }, { "Key2", someObject } });
Since C# 7.0, you can use value tuples. C# 7.0 not only introduces a new type but a simplified syntax for tuple types and for tuple values. A tuple type is simply written as a list of types surrounded by braces:
(string, int, double)
The corresponding elements are named Item1, Item2, Item2. You can also specify optional aliases. These aliases are only syntactic sugar (a trick of the C# compiler); the tuples are still based on the invariant (but generic) System.ValueTuple<T1, T2, ...> struct.
(string name, int count, double magnitude)
Tuple values have a similar syntax, except that you specify expressions instead of types
("test", 7, x + 5.91)
or with the aliases
(name: "test", count: 7, magnitude: x + 5.91)
Example with params array:
public static void MyFunction(params (string Key, object Value)[] pairs)
{
foreach (var pair in pairs) {
Console.WriteLine($"{pair.Key} = {pair.Value}");
}
}
It is also possible to deconstruct a tuple like this
var (key, value) = pair;
Console.WriteLine($"{key} = {value}");
This extracts the items of the tuple in two separate variables key and value.
Now, you can call MyFunction with a varying number of arguments easily:
MyFunction(("a", 1), ("b", 2), ("c", 3));
It allows us to do things like
DrawLine((0, 0), (10, 0), (10, 10), (0, 10), (0, 0));
See: New Features in C# 7.0
Funny, I just created (minutes ago) a method that allows to do that, using anonymous types and reflection :
MyMethod(new { Key1 = "value1", Key2 = "value2" });
public void MyMethod(object keyValuePairs)
{
var dic = DictionaryFromAnonymousObject(keyValuePairs);
// Do something with the dictionary
}
public static IDictionary<string, string> DictionaryFromAnonymousObject(object o)
{
IDictionary<string, string> dic = new Dictionary<string, string>();
var properties = o.GetType().GetProperties();
foreach (PropertyInfo prop in properties)
{
dic.Add(prop.Name, prop.GetValue(o, null) as string);
}
return dic;
}
A bit of a hack, but you could have your Message class implement the IEnumerable interface and give it an Add method. You'll then be able to use collection initializer syntax:
Agent.SendMessage
(
new Message(MessageTypes.SomethingHappened) {{ "foo", 42 }, { "bar", 123 }}
);
// ...
public class Message : IEnumerable
{
private Dictionary<string, object> _map = new Dictionary<string, object>();
public Message(MessageTypes mt)
{
// ...
}
public void Add(string key, object value)
{
_map.Add(key, value);
}
IEnumerator IEnumerable.GetEnumerator()
{
return ((IEnumerable)_map).GetEnumerator();
// or throw a NotImplementedException if you prefer
}
}
Using a dictionary:
myFunction(new Dictionary<string, object>(){
{"Key", value},
{"Key2", value}});
Which is straight forward, you need only one new Dictionary<K, V>, not for each argument. It's trivial to get the keys and values.
Or with an anonymous type:
myFunction(new {
Key = value,
Key2 = value});
Which is not very nice to use inside the function, you'll need reflection. This would look something like this:
foreach (PropertyInfo property in arg.GetType().GetProperties())
{
key = property.Name;
value = property.GetValue(arg, null);
}
(Staight from my head, probably some errors...)
Use a Dictionary ...
void Main()
{
var dic = new Dictionary<string, object>();
dic.Add( "Key1", 1 );
dic.Add( "Key2", 2 );
MyFunction( dic ).Dump();
}
public static object MyFunction( IDictionary dic )
{
return dic["Key1"];
}
Here's more of the same:
static void Main(string[] args)
{
// http://msdn.microsoft.com/en-us/library/bb531208.aspx
MyMethod(new Dictionary<string,string>()
{
{"key1","value1"},
{"key2","value2"}
});
}
static void MyMethod(Dictionary<string, string> dictionary)
{
foreach (string key in dictionary.Keys)
{
Console.WriteLine("{0} - {1}", key, dictionary[key]);
}
}
Some details on initialising a dictionary can be found here.
With dynamic type in C# 4.0:
public class MyClass
{
// Could use another generic type if preferred
private readonly Dictionary<string, dynamic> _dictionary = new Dictionary<string, dynamic>();
public void MyFunction(params dynamic[] kvps)
{
foreach (dynamic kvp in kvps)
_dictionary.Add(kvp.Key, kvp.Value);
}
}
Call using:
MyFunction(new {Key = "Key1", Value = "Value1"}, new {Key = "Key2", Value = "Value2"});
You can do that:
TestNamedMethod(DateField => DateTime.Now, IdField => 3);
where DateField and IdField are supposed to be a 'string' identifiers.
The TestNameMethod:
public static string TestNameMethod(params Func<object, object>[] args)
{
var name = (args[0].Method.GetParameters()[0]).Name;
var val = args[0].Invoke(null);
var name2 = (args[1].Method.GetParameters()[0]).Name;
var val2 = args[1].Invoke(null);
Console.WriteLine("{0} : {1}, {2} : {3}", name, val, name2, val2);
}
Performance is 5% faster than using Dictionary. Disadvantage: you can't use variable as a key.
You could also reference the nugetpackage "valuetuple", which allows you to do the following:
public static void MyFunction(params ValueTuple<string, object>[] pairs)
{
var pair = pairs[1];
var stringValue = pair.item1;
var objectValue = pair.item2;
}
You can then call the method like this:
MyFunction(("string",object),("string", object));
You could use Tuples to achieve something similar to #Bryan Watts's Pairing.Of without the extra class:
public static void MyFunction(params Tuple<string, int>[] pairs)
{
}
MyFunction(Tuple.Create("foo", 1), Tuple.Create("bar", 2));
So I'm new and can't currently add comments, but this is just a suggestion to improve #Bryan Watts's idea of the Pairing.of class by making it generic, allowing it to be easily used by other classes.
public class Pairing
{
public static KeyValuePair<TKey, TValue> of<TKey, TValue>(TKey key, TValue value)
{
return new KeyValuePair<TKey, TValue>(key, value);
}
}