Getting values of a generic IDictionary using reflection - c#

I have an instance that implements IDictionary<T, K>, I don't know T and K at compiletime, and want to get all elements from it. I don't want to use IEnumerable for some reason, which would be the only non-generic interface implemented by IDictionary.
Code I have so far:
// getting types
Type iDictType = instance.GetType().GetInterface("IDictionary`2");
Type keyType = iDictType.GetGenericArguments()[0];
Type valueType = iDictType.GetGenericArguments()[1];
// getting the keys
IEnumerable keys = (IEnumerable)dictType.GetProperty("Keys")
.GetValue(instance, null);
foreach (object key in keys)
{
// ==> this does not work: calling the [] operator
object value = dictType.GetProperty("Item")
.GetValue(instance, new object[] {key } );
// getting the value from another instance with TryGet
MethodInfo tryGetValue = iDictType.GetMethod("TryGetValue");
object[] arguments = new object[] { key, null };
bool hasElement = (bool)tryGetValue.Invoke(otherInstance, arguments);
object anotherValue = arguments[1];
}
I could also call TryGetValue, but I think it should be possible to call the [] operator. Can anybody help me?

It would be better to figure out the TKey / TValue, and switch into regular code via MakeGenericMethod - like so:
(edit - you could pass in the otherInstance as an argument too, if they are of the same type)
static class Program
{
static void Main()
{
object obj = new Dictionary<int, string> {
{ 123, "abc" }, { 456, "def" } };
foreach (Type iType in obj.GetType().GetInterfaces())
{
if (iType.IsGenericType && iType.GetGenericTypeDefinition()
== typeof(IDictionary<,>))
{
typeof(Program).GetMethod("ShowContents")
.MakeGenericMethod(iType.GetGenericArguments())
.Invoke(null, new object[] { obj });
break;
}
}
}
public static void ShowContents<TKey, TValue>(
IDictionary<TKey, TValue> data)
{
foreach (var pair in data)
{
Console.WriteLine(pair.Key + " = " + pair.Value);
}
}
}

Just for completion, even if Marc Gravell's solution is much nicer, this is the way how it works the way I already started:
object value = dictType.GetMethod("get_Item")
.Invoke(instance, new object[] { key });
This calls the [] operator of the dictionary.

Related

Call type method return exception [duplicate]

This question already has answers here:
What is a NullReferenceException, and how do I fix it?
(27 answers)
Closed 5 years ago.
I have a string which holds some words that i will be changing later by my methods :
example :
Hello $name How are you doing ?
i'm $ask
i will be changing $name and $ask using a method :
public static string ReturnLetter<T>(Dictionary<string, T> value)
{
var letter = File.ReadAllText("LETTER.html");
var newletter = "";
foreach (var type in value)
{
var item = type.Value;
var newstring = item.GetType().GetMethod("ReturnValue").Invoke(null, new object[] { }).ToString();
newletter = letter.Replace(type.Key, newstring);
}
return newletter;
}
i'm returning the Dictionary values with :
public static Dictionary<string, Type> returnDic()
{
var types = GetTypesInNamespace(Assembly.GetExecutingAssembly(), "ConsoleApplication11.Packets");
var Dic = new Dictionary<string, Type>();
foreach (var t in types)
{
Dic.Add(t.Name, t);
}
return Dic;
}
Types i holds is :
class name
{
private static string[] names =
{
"miri",
"sara",
};
public static string ReturnValue()
{
var random = new Random();
return names[random.Next(0, 1)];
}
}
I'm getting exception (null references) at
var newstring = item.GetType().GetMethod("ReturnValue").Invoke(null, new object[] { }).ToString();
i couldn't really track where the error is coming from !
i'm stuck at this for like an hour now
any help is appreciated
EDIT :
in the method which works :
public static string ReturnValue<T>()
{
return typeof(T).GetMethod("ReturnValue").Invoke(null,new object[] { }).ToString();
}
and then i realized that item.GetType() return RunTimeTime Instead of which typeof(T) returns .
As item seems to be a Type, try replacing
item.GetType().GetMethod("ReturnValue")...
with
item.GetMethod("ReturnValue")...
You were trying to find that method on class Type instead of class name. As it doesn't exist there, you get a null.
But a better architecture would use instance methods and interfaces. No reflection needed to invoke methods then.
I finally got to notice the issue !
in the method :
public static Dictionary<string, Type> returnDic()
{
var types = GetTypesInNamespace(Assembly.GetExecutingAssembly(), "ConsoleApplication11.Packets");
var Dic = new Dictionary<string, Type>();
foreach (var t in types)
{
Dic.Add(t.Name, t);
}
return Dic;
}
I'm returning a Type and not a T type and in the Replace Method i'm getting the Type of Type
foreach (var type in value)
{
var item = type.Value;
var newstring = item.GetType().GetMethod("ReturnValue").Invoke(null, new object[] { }).ToString(); ////// GetType() of Type
newletter = letter.Replace(type.Key, newstring);
}
i solved it by simple Converting type.Value to Type
var item = type.Value as Type;

How should I make a deep copy of a Dictionary? [duplicate]

I've got a generic dictionary Dictionary<string, T> that I would like to essentially make a Clone() of ..any suggestions.
(Note: although the cloning version is potentially useful, for a simple shallow copy the constructor I mention in the other post is a better option.)
How deep do you want the copy to be, and what version of .NET are you using? I suspect that a LINQ call to ToDictionary, specifying both the key and element selector, will be the easiest way to go if you're using .NET 3.5.
For instance, if you don't mind the value being a shallow clone:
var newDictionary = oldDictionary.ToDictionary(entry => entry.Key,
entry => entry.Value);
If you've already constrained T to implement ICloneable:
var newDictionary = oldDictionary.ToDictionary(entry => entry.Key,
entry => (T) entry.Value.Clone());
(Those are untested, but should work.)
Okay, the .NET 2.0 answers:
If you don't need to clone the values, you can use the constructor overload to Dictionary which takes an existing IDictionary. (You can specify the comparer as the existing dictionary's comparer, too.)
If you do need to clone the values, you can use something like this:
public static Dictionary<TKey, TValue> CloneDictionaryCloningValues<TKey, TValue>
(Dictionary<TKey, TValue> original) where TValue : ICloneable
{
Dictionary<TKey, TValue> ret = new Dictionary<TKey, TValue>(original.Count,
original.Comparer);
foreach (KeyValuePair<TKey, TValue> entry in original)
{
ret.Add(entry.Key, (TValue) entry.Value.Clone());
}
return ret;
}
That relies on TValue.Clone() being a suitably deep clone as well, of course.
Dictionary<string, int> dictionary = new Dictionary<string, int>();
Dictionary<string, int> copy = new Dictionary<string, int>(dictionary);
That's what helped me, when I was trying to deep copy a Dictionary < string, string >
Dictionary<string, string> dict2 = new Dictionary<string, string>(dict);
Good luck
For .NET 2.0 you could implement a class which inherits from Dictionary and implements ICloneable.
public class CloneableDictionary<TKey, TValue> : Dictionary<TKey, TValue> where TValue : ICloneable
{
public IDictionary<TKey, TValue> Clone()
{
CloneableDictionary<TKey, TValue> clone = new CloneableDictionary<TKey, TValue>();
foreach (KeyValuePair<TKey, TValue> pair in this)
{
clone.Add(pair.Key, (TValue)pair.Value.Clone());
}
return clone;
}
}
You can then clone the dictionary simply by calling the Clone method. Of course this implementation requires that the value type of the dictionary implements ICloneable, but otherwise a generic implementation isn't practical at all.
This works fine for me
// assuming this fills the List
List<Dictionary<string, string>> obj = this.getData();
List<Dictionary<string, string>> objCopy = new List<Dictionary<string, string>>(obj);
As Tomer Wolberg describes in the comments, this does not work if the value type is a mutable class.
You could always use serialization. You could serialize the object then deserialize it. That will give you a deep copy of the Dictionary and all the items inside of it. Now you can create a deep copy of any object that is marked as [Serializable] without writing any special code.
Here are two methods that will use Binary Serialization. If you use these methods you simply call
object deepcopy = FromBinary(ToBinary(yourDictionary));
public Byte[] ToBinary()
{
MemoryStream ms = null;
Byte[] byteArray = null;
try
{
BinaryFormatter serializer = new BinaryFormatter();
ms = new MemoryStream();
serializer.Serialize(ms, this);
byteArray = ms.ToArray();
}
catch (Exception unexpected)
{
Trace.Fail(unexpected.Message);
throw;
}
finally
{
if (ms != null)
ms.Close();
}
return byteArray;
}
public object FromBinary(Byte[] buffer)
{
MemoryStream ms = null;
object deserializedObject = null;
try
{
BinaryFormatter serializer = new BinaryFormatter();
ms = new MemoryStream();
ms.Write(buffer, 0, buffer.Length);
ms.Position = 0;
deserializedObject = serializer.Deserialize(ms);
}
finally
{
if (ms != null)
ms.Close();
}
return deserializedObject;
}
The best way for me is this:
Dictionary<int, int> copy= new Dictionary<int, int>(yourListOrDictionary);
Binary Serialization method works fine but in my tests it showed to be 10x slower than a non-serialization implementation of clone. Tested it on Dictionary<string , List<double>>
Try this if key/values are ICloneable:
public static Dictionary<K,V> CloneDictionary<K,V>(Dictionary<K,V> dict) where K : ICloneable where V : ICloneable
{
Dictionary<K, V> newDict = null;
if (dict != null)
{
// If the key and value are value types, just use copy constructor.
if (((typeof(K).IsValueType || typeof(K) == typeof(string)) &&
(typeof(V).IsValueType) || typeof(V) == typeof(string)))
{
newDict = new Dictionary<K, V>(dict);
}
else // prepare to clone key or value or both
{
newDict = new Dictionary<K, V>();
foreach (KeyValuePair<K, V> kvp in dict)
{
K key;
if (typeof(K).IsValueType || typeof(K) == typeof(string))
{
key = kvp.Key;
}
else
{
key = (K)kvp.Key.Clone();
}
V value;
if (typeof(V).IsValueType || typeof(V) == typeof(string))
{
value = kvp.Value;
}
else
{
value = (V)kvp.Value.Clone();
}
newDict[key] = value;
}
}
}
return newDict;
}
In the case you have a Dictionary of "object" and object can be anything like (double, int, ... or ComplexClass):
Dictionary<string, object> dictSrc { get; set; }
public class ComplexClass : ICloneable
{
private Point3D ...;
private Vector3D ....;
[...]
public object Clone()
{
ComplexClass clone = new ComplexClass();
clone = (ComplexClass)this.MemberwiseClone();
return clone;
}
}
dictSrc["toto"] = new ComplexClass()
dictSrc["tata"] = 12.3
...
dictDest = dictSrc.ToDictionary(entry => entry.Key,
entry => ((entry.Value is ICloneable) ? (entry.Value as ICloneable).Clone() : entry.Value) );
Here is some real "true deep copying" without knowing type with some recursive walk, good for the beginnig. It is good for nested types and almost any tricky type I think. I did not added nested arrays handling yet, but you can modify it by your choice.
Dictionary<string, Dictionary<string, dynamic>> buildInfoDict =
new Dictionary<string, Dictionary<string, dynamic>>()
{
{"tag",new Dictionary<string,dynamic>(){
{ "attrName", "tag" },
{ "isCss", "False" },
{ "turnedOn","True" },
{ "tag",null }
} },
{"id",new Dictionary<string,dynamic>(){
{ "attrName", "id" },
{ "isCss", "False" },
{ "turnedOn","True" },
{ "id",null }
} },
{"width",new Dictionary<string,dynamic>(){
{ "attrName", "width" },
{ "isCss", "True" },
{ "turnedOn","True" },
{ "width","20%" }
} },
{"height",new Dictionary<string,dynamic>(){
{ "attrName", "height" },
{ "isCss", "True" },
{ "turnedOn","True" },
{ "height","20%" }
} },
{"text",new Dictionary<string,dynamic>(){
{ "attrName", null },
{ "isCss", "False" },
{ "turnedOn","True" },
{ "text","" }
} },
{"href",new Dictionary<string,dynamic>(){
{ "attrName", null },
{ "isCss", "False" },
{ "flags", "removeAttrIfTurnedOff" },
{ "turnedOn","True" },
{ "href","about:blank" }
} }
};
var cln=clone(buildInfoDict);
public static dynamic clone(dynamic obj)
{
dynamic cloneObj = null;
if (IsAssignableFrom(obj, typeof(IDictionary)))
{
cloneObj = Activator.CreateInstance(obj.GetType());
foreach (var key in obj.Keys)
{
cloneObj[key] = clone(obj[key]);
}
}
else if (IsNumber(obj) || obj.GetType() == typeof(string))
{
cloneObj = obj;
}
else
{
Debugger.Break();
}
return cloneObj;
}
public static bool IsAssignableFrom(this object obj, Type ObjType = null, Type ListType = null, bool HandleBaseTypes = false)
{
if (ObjType == null)
{
ObjType = obj.GetType();
}
bool Res;
do
{
Res = (ObjType.IsGenericType && ObjType.GetGenericTypeDefinition().IsAssignableFrom(ListType)) ||
(ListType == null && ObjType.IsAssignableFrom(obj.GetType()));
ObjType = ObjType.BaseType;
} while ((!Res && ObjType != null) && HandleBaseTypes && ObjType != typeof(object));
return Res;
}
public static bool IsNumber(this object value)
{
return value is sbyte
|| value is byte
|| value is short
|| value is ushort
|| value is int
|| value is uint
|| value is long
|| value is ulong
|| value is float
|| value is double
|| value is decimal;
}
Here is another way to clone a dictionary, assuming you know to do the "right" thing as far as handling whatever is hiding behind the "T" (a.k.a. "object") in your specific circumstances.
internal static Dictionary<string, object> Clone(Dictionary<string, object> dictIn)
{
Dictionary<string, object> dictOut = new Dictionary<string, object>();
IDictionaryEnumerator enumMyDictionary = dictIn.GetEnumerator();
while (enumMyDictionary.MoveNext())
{
string strKey = (string)enumMyDictionary.Key;
object oValue = enumMyDictionary.Value;
dictOut.Add(strKey, oValue);
}
return dictOut;
}
I would evaluate if T was a value or reference type. In the case T was a value type I would use the constructor of Dictionary, and in the case when T was a reference type I would make sure T inherited from ICloneable.
It will give
private static IDictionary<string, T> Copy<T>(this IDictionary<string, T> dict)
where T : ICloneable
{
if (typeof(T).IsValueType)
{
return new Dictionary<string, T>(dict);
}
else
{
var copy = new Dictionary<string, T>();
foreach (var pair in dict)
{
copy[pair.Key] = pair.Value;
}
return copy;
}
}

C# Generic Class field possible

I am new to C# (Java developer), I want to have a class field that is a generic list, actually it is a dictionary of lists:
protected IDictionary<String, IList<Object>> filters;
I have code that sets
public void SetFilters(String key, params Object[] values) {
if (key == null || values == null) {
throw new ArgumentNullException("Must have filter name and values.");
}
if (filters == null) filters = new Dictionary<String, IList<Object>>();
IList<Object> fvalues = values.ToList();
filters.Add(key, fvalues);
}
But when my code tries to retrieve and cast the IList<Object> back to IList<String> or IList<int> I get an InvalidCastException.
I thought I would make the list generic:
protected IDictionary<String, IList<T>> filters; //does not complile
protected IDictionary<String, IList<T>> filters where T: Object;//does not compile either
I cannot make the class generic since the dictionary will have lists of Strings or int. In Java, Integer and Strings are all Objects, so this was not an issue with IList<? extends Object>.
Thanks!
You could use System.Collection.IList, similar to this:
public class Foo
{
public IDictionary<String, IList> filters;
public void SetFilters(String key, params object[] values)
{
if (key == null || values == null)
{
throw new ArgumentNullException("Must have filter name and values.");
}
if (filters == null)
{
filters = new Dictionary<String, IList>();
}
IList fvalues = values.ToList();
filters.Add(key, fvalues);
}
}
You could then use it like this:
var foo = new Foo();
foo.SetFilters("Key1", 1,2,3);
foo.SetFilters("Key2", "a","b","c");
foo.SetFilters("Key3", new {a = 1, b = 2}, new {c = 1, d = 2});
You still have then the issue of casting back each list type into the expected type when accessing and using it.
DEMO - Using IList
Does it help? I created a new generic class.
internal class Program
{
private class Reed<T>
{
private IDictionary<String, IList<T>> filters;
public void SetFilters(String key, params T[] values)
{
if (key == null || values == null)
{
throw new ArgumentNullException("Must have filter name and values.");
}
if (filters == null)
filters = new Dictionary<String, IList<T>>();
IList<T> fvalues = values.ToList();
filters.Add(key, fvalues);
}
}
private static void Main(string[] args)
{
var r1 = new Reed<string>();
r1.SetFilters("test", "one", "two", "three");
var r2 = new Reed<int>();
r2.SetFilters("test", 1, 2, 3);
}
}
In order to avoid the error casting, you need to create a typed list in the first place. To do that you should use a generic.
For the dictionary item type, you can use IList or object but you will need to store a strongly typed list as the item value.
If you make SetFilters a generic method, then it can make a properly typed list to store in the dictionary. I included a GetFilters method that returns the list that matches the key. i.e. values.ToList() will create a List<T>
public class FilterManager
{
protected IDictionary<String, IList> filters = new Dictionary<string, IList>();
public void SetFilters<T>(String key, params T[] values)
{
if (key == null || values == null)
{
throw new ArgumentNullException("Must have filter name and values.");
}
IList fvalues = values.ToList();
filters.Add(key, fvalues);
}
public IList<T> GetFilters<T>(string key)
{
return (IList<T>)filters[key];
}
}
Call it like this
var filterManager = new FilterManager();
filterManager.SetFilters("MyIntegerFilters", 3, 4, 5);
filterManager.SetFilters("MyStringFilters", "A", "B", "C");
var intFilters = filterManager.GetFilters<int>("MyIntegerFilters");
var stringFilters = filterManager.GetFilters<string>("MyStringFilters");
You will get an exception if you call
var filters = filterManager.GetFilters<int>("MyStringFilters");
because it will try to convert a List<string> to a List<int>

get dictionary value using .(dot) operator using dynamic keyword, it's possible?

I have the following class:
public class foo
{
public Dictionary<string, string> data = new Dictionary<string, string>();
public foo(params object[] args)
{
foreach (object arg in args)
{
data.Add(arg.ToString(), "..");
}
}
}
I need get the value of dictionary using the dot operadotor it's because the class that I set the class as arguments use the dynamic keyword to "walk" on the class.
for example:
var args = new[] {"a","b","c"};
var Foo = new foo(args);
var baa = Foo.data.a;
Console.Write(baa); // ..
if exists an way to make dynamic variables, something like:
public foo(params object[] args) {
foreach (object arg in args) {
var name = (string) arg;
var value = "..";
MakeVariable(name, value);
}
}
makes an variable named of arg and the value .. as public member of foo class.
anyway differents to solve this is very appreciated. Thanks in advance.
You can have Foo inherit from DynamicObject:
public class Foo : DynamicObject
{
private Dictionary<string, string> data = new Dictionary<string, string>();
public Foo(params object[] args)
{
foreach (object arg in args)
{
data.Add(arg.ToString(), "..");
}
}
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
if (data.ContainsKey(binder.Name))
{
result = data[binder.Name];
return true;
}
return base.TryGetMember(binder, out result);
}
}
To use it you can use dynamic to hold an instance of Foo:
var args= new[] { "a", "b", "c" };
dynamic foo = new Foo(args);
var myA = foo.a; //returns ".."
Keep in mind that you will lose type safety since you have to use dynamic - your use case should really justify this disadvantage - usually there is a better approach.
I think you should use DynamicObject. If you are using an older version of the framework the only option is Reflection.Emit
The dynamic works something like this
// If you try to get a value of a property
// not defined in the class, this method is called.
public override bool TryGetMember(
GetMemberBinder binder, out object result)
{
// Converting the property name to lowercase
// so that property names become case-insensitive.
string name = binder.Name.ToLower();
// If the property name is found in a dictionary,
// set the result parameter to the property value and return true.
// Otherwise, return false.
return dictionary.TryGetValue(name, out result);
}
// If you try to set a value of a property that is
// not defined in the class, this method is called.
public override bool TrySetMember(
SetMemberBinder binder, object value)
{
// Converting the property name to lowercase
// so that property names become case-insensitive.
dictionary[binder.Name.ToLower()] = value;
// You can always add a value to a dictionary,
// so this method always returns true.
return true;
}
Another option is to use the ExpandoObject class if you want to directly expose the Data member, as in your example. This keeps the code simpler if you don't need to define specific operations that would require inheriting DynamicObject.
public class Foo
{
public dynamic Data = new ExpandoObject();
public Foo(params object[] args)
{
var dataDict = (IDictionary<string, object>)Data;
foreach (var obj in args)
{
dataDict.Add(obj.ToString(), "..");
}
}
}
Usage:
var foo = new Foo("a", "b", "c");
Console.WriteLine(foo.Data.a);
((IDictionary<string, object>)foo.Data).Add("d", "!");
foreach (var item in foo.Data)
{
Console.WriteLine("{0} : {1}", item.Key, item.Value);
}
Notice that I cast to a dictionary and added "d" although I could've also assigned it directly: foo.Data.d = "!". The only difference is you may not know ahead of time what field names you have, and the former example allows you to setup the ExpandoObject based on dynamic input, whereas the latter is useful when you already know what field name to use.
In .NET 4 this exact behavior is implemented by ExpandoObject class:
public class Foo
{
private readonly ExpandoObject _dict = new ExpandoObject();
public dynamic Data
{
get { return _dict; }
}
public Foo(params object[] args)
{
foreach (var arg in args)
_dict.Add(arg.ToString(), "..");
}
}
var foo = new Foo("a", "b", "c");
foo.Data.x = 3.14;
Console.Write(foo.Data.a);

Initialize an object of type T from Dictionary<string,object>

I'm looking for more generic/"standard" way to instantiate object of some type T from set of pairs string,object. For me it looks like there should be some well known way to do it but I cannot find it, so I come up with this piece of code.
Does anybody know something better?
// usage
public class test
{
public int field1;
public string field2;
public bool field3;
public string[] field4;
public IDictionary<string,object> field5 { get; set; }
public static IDictionary<string,object> dynamic()
{
return new Dictionary<string,object>{
{ "field1", 2 },
{ "field2", "string" },
{ "field3", true },
{ "field4", new[] { "id3", "id4", "id5" } },
{ "field5", new Dictionary<string,object>{ { "id1", "" } } }
};
}
}
...
var r = new dynamic_data_serializer<test>().create( test.dynamic() );
...
//
public class dynamic_data_serializer< T >
{
public T create( object obj )
{
var result = default(T);
if ( obj == null )
return result;
var ttype = typeof(T);
var objtype = obj.GetType();
if ( ttype.IsAssignableFrom( objtype ) ) {
result = (T)obj;
return result;
}
if ( ttype.IsClass ) { // custom classes, array, dictionary, etc.
result = Activator.CreateInstance<T>();
if ( objtype == typeof(IDictionary<string,object>) ||
objtype == typeof(Dictionary<string,object>) ) {
var obj_as_dict = obj as IDictionary<string,object>;
var fields = ttype.GetFields();
if ( fields.Length > 0 )
set_fields_from( result, fields, obj_as_dict );
var properties = ttype.GetProperties();
if ( properties.Length > 0 )
set_properties_from( result, properties, obj_as_dict );
}
}
return result;
}
private void set_fields_from( T _this_, FieldInfo[] fields, IDictionary<string,object> obj ) {
foreach ( var fld in fields ) {
var v = find( obj, fld.Name );
if ( v != null ) {
var mobj = call_deserialize( fld.FieldType, v );
fld.SetValue( _this_, mobj );
}
}
}
private void set_properties_from( T _this_, PropertyInfo[] properties, IDictionary<string,object> obj ) {
foreach ( var prop in properties ) {
var v = find( obj, prop.Name );
if ( v != null ) {
var mobj = call_deserialize( prop.PropertyType, v );
prop.SetValue( _this_, mobj, null );
}
}
}
private object find( IDictionary<string,object> obj, string name ) {
foreach ( var kv in obj )
if ( string.Compare( kv.Key, name, true ) == 0 )
return kv.Value;
return null;
}
private object call_deserialize( Type des_type, object value ) {
var gtype = typeof(dynamic_data_serializer<>);
Type desz_type = gtype.MakeGenericType( new[]{ des_type } );
object desz = Activator.CreateInstance( desz_type );
var method_type = desz_type.GetMethod( "create" );
return method_type.Invoke( desz, new[]{ value } );
}
}
}
DataContractJsonSerializer is too slow, but you're using reflection? If you have to deserialize lots of objects, I would recommend using compiled lambdas instead of reflection. A lambda can only set properties, not fields (at least in .Net 3.5), so you may have to adjust the classes you use it on, but it's worth it because it's like 1000 times faster.
Here's a function that creates a property setter given a type and a PropertyInfo for the property to set:
static Action<object, TValue> MakeSetter<TValue>(Type tclass, PropertyInfo propInfo)
{
var t = lambda.Expression.Parameter(typeof(object), "t");
var v = lambda.Expression.Parameter(typeof(TValue), "v");
// return (t, v) => ((tclass)t).prop = (tproperty)v
return (Action<object, TValue>)
lambda.Expression.Lambda(
lambda.Expression.Call(
lambda.Expression.Convert(t, tclass),
propInfo.GetSetMethod(),
lambda.Expression.Convert(v, propInfo.PropertyType)),
t,
v)
.Compile();
}
You would have a dictionary of setters for each class, and whenever you have to set a property of a class, you would look up the setter for that property in the dictionary and call it with the value to assign, like this: setters[propName](_this_, value);
I might suggest FormatterServices.PopulateObjectMembers, except a: this is still slow AFAIK, and b: I tried it (below) and it seems to want to throw an exception on the property (don't know why; didn't look too deep). Another option may be Expression, but you don't really want to do the Compile each time (better to do it once only and cache it, but that demands a known format).
public T create(object obj)
{ // simplified for illustration
var bindings = obj as IDictionary<string, object>;
Type type = typeof(T);
var func = Expression.Lambda<Func<T>>(Expression.MemberInit(
Expression.New(type),
from pair in bindings
let member = type.GetMember(pair.Key).Single()
select (MemberBinding)Expression.Bind(member, Expression.Constant(pair.Value))));
return func.Compile().Invoke();
}
Finally, you might cache a set of pre-compiled Action<object> setters (keyed against the member name). In reality this is probably your best bet. Properties are easy (you use Delegate.CreateDelegate) - fields might need DynamicMethod - but if you can't predict the layout in advance it'll have the least overhead.
For the keyed / IL approach (you won't get faster):
public class dynamic_data_serializer<T>
{
public T create(object obj)
{
T inst = Activator.CreateInstance<T>();
var bindings = obj as IDictionary<string, object>;
foreach (var pair in bindings)
{
setters[pair.Key](inst, pair.Value);
}
return inst;
}
private static readonly Dictionary<string, Action<T, object>> setters;
static dynamic_data_serializer()
{
setters = new Dictionary<string, Action<T, object>>(StringComparer.Ordinal);
foreach (PropertyInfo prop in typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance)) {
setters.Add(prop.Name, CreateForMember(prop));
}
foreach (FieldInfo field in typeof(T).GetFields(BindingFlags.Public | BindingFlags.Instance)) {
setters.Add(field.Name, CreateForMember(field));
}
}
static Action<T, object> CreateForMember(MemberInfo member)
{
bool isField;
Type type;
switch (member.MemberType) {
case MemberTypes.Property:
isField = false;
type = ((PropertyInfo)member).PropertyType;
break;
case MemberTypes.Field:
isField = true;
type = ((FieldInfo)member).FieldType;
break;
default:
throw new NotSupportedException();
}
DynamicMethod method = new DynamicMethod("__set_" + member.Name, null, new Type[] { typeof(T), typeof(object) });
ILGenerator il = method.GetILGenerator();
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldarg_1);
if(type != typeof(object)) {
il.Emit(type.IsValueType ? OpCodes.Unbox_Any : OpCodes.Castclass, type);
}
if (isField) {il.Emit(OpCodes.Stfld, (FieldInfo)member);}
else { il.EmitCall(OpCodes.Callvirt, ((PropertyInfo)member).GetSetMethod(), null); }
il.Emit(OpCodes.Ret);
return (Action<T, object>)method.CreateDelegate(typeof(Action<T, object>));
}
}
DataContractJsonSerializer
Why would you make a custom serializer and not use the DataContractJsonSerializer?
EDIT
If DataContractJsonSerializer doesn't fit you, you can try JSON.Net. Implementing a serializer efficiently is not an easy task, there's a lot of pitfalls and special cases that you may not want to get into. By the way, your code sample makes heavy use of reflection which is slow, I doubt that it will perform better than DataContractJsonSerializer or JSON.Net.

Categories