Expression to Call a Method on Each Property of a Class - c#

I want to take a class, loop through it's properties, get the property value, and call a method passing that property value in. I think I can get the property values, but what does the lambda expression's body look like? What body is used to call a method on each property?
This is what I have so far...
Action<T> CreateExpression<T>( T obj )
{
foreach( var property in typeof( T ).GetProperties() )
{
Expression value = Expression.Property( Expression.Constant( obj ), property );
var method = Expression.Call( typeof( SomeType ), "SomeMethod", null, value );
}
// What expression body can be used that will call
// all the method expressions for each property?
var body = Expression...
return Expression.Lambda<Action<T>>( body, ... ).Compile();
}

It depends on a few things.
does the method return anything? Expression in 3.5 can't do multiple separate "action" operations (a statement body), but you can cheat if you can do something with a fluent API:
SomeMethod(obj.Prop1).SomeMethod(obj.Prop2).SomeMethod(obj.Prop3);
(perhaps using generics to make it simpler)
do you have access to 4.0? In 4.0 there are additional Expression types allowing statement bodies and exactly what you ask for. I discuss some similar examples in an article here (look for Expression.Block, although this is based on a beta a while ago - it may have been renamed by now).
Alternative; since you are compiling to a delegate, consider that an Action<T> is multicast; you could build a set of simple operations, and combine them in the delegate; this would work in 3.5; for example:
using System;
using System.Linq.Expressions;
static class SomeType
{
static void SomeMethod<T>(T value)
{
Console.WriteLine(value);
}
}
class Customer
{
public int Id { get; set; }
public string Name { get; set; }
}
static class Program
{
static readonly Action<Customer> action = CreateAction<Customer>();
static void Main()
{
Customer cust = new Customer { Id = 123, Name = "Abc" };
action(cust);
}
static Action<T> CreateAction<T>()
{
Action<T> result = null;
var param = Expression.Parameter(typeof(T), "obj");
foreach (var property in typeof(T).GetProperties(
BindingFlags.Instance | BindingFlags.Public))
{
if (property.GetIndexParameters().Length > 0) continue;
var propVal = Expression.Property(param, property);
var call = Expression.Call(typeof(SomeType), "SomeMethod", new Type[] {propVal.Type}, propVal);
result += Expression.Lambda<Action<T>>(call, param).Compile();
}
return result;
}
}

I dont think it will be so easy using Expressions, in .NET 3.5 at least.
.NET 4 supports a block construct I believe.
I suggest using Reflection.Emit rather.
Here is a starting point (for fields but can be changed easily):
internal 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>>> fieldcache =
new Dictionary<Type, Func<object, Dictionary<string, object>>>();
static Dictionary<string, object> GetFields(object o)
{
var t = o.GetType();
Func<object, Dictionary<string, object>> getter;
if (!fieldcache.TryGetValue(t, out getter))
{
var rettype = typeof(Dictionary<string, object>);
var dm = new DynamicMethod(t.Name + ":GetFields",
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 field in t.GetFields(
BindingFlags.DeclaredOnly |
BindingFlags.Instance |
BindingFlags.Public |
BindingFlags.NonPublic))
{
if (!field.FieldType.IsSubclassOf(typeof(Component)))
{
continue;
}
ilgen.Emit(OpCodes.Ldloc, dict);
ilgen.Emit(OpCodes.Ldstr, field.Name);
ilgen.Emit(OpCodes.Ldloc, instance);
ilgen.Emit(OpCodes.Ldfld, field);
ilgen.Emit(OpCodes.Castclass, typeof(object));
ilgen.Emit(OpCodes.Callvirt, add);
}
ilgen.Emit(OpCodes.Ldloc, dict);
ilgen.Emit(OpCodes.Ret);
fieldcache[t] = getter = dm.CreateDelegate<Func<object,
Dictionary<string, object>>>();
}
return getter(o);
}

Use the Block statement. The code below for example writes out the names of all properties
static void WritePropertyNames()
{
TestObject lTestObject = new TestObject();
PropertyInfo[] lProperty = typeof(TestObject).GetProperties();
List<Expression> lExpressions = new List<Expression>();
MethodInfo lMethodInfo = typeof(Console).GetMethod("WriteLine", new Type[] { typeof(string) });
lProperty.ForEach(x =>
{
ConstantExpression lConstant = Expression.Constant(x.Name);
MethodCallExpression lMethodCall = Expression.Call(lMethodInfo, lConstant);
lExpressions.Add(lMethodCall);
});
BlockExpression lBlock = Expression.Block(lExpressions);
LambdaExpression lLambda = Expression.Lambda<Action>(lBlock, null);
Action lWriteProperties = lLambda.Compile() as Action;
lWriteProperties();
}

Expression trees can only contain a single statement. To do what you are trying you would need to Expression.Lambda<>() in your loop, passing "method" as the body.
I believe this has changed in .NET Framework 4.0.
Andrew

If you're willing to have your method SomeType.SomeMethod accept an object[] then you can do something like this (note that indexers can not be handled here so we discard them):
using System;
using System.Collections.Generic;
using System.Linq.Expressions;
namespace Test {
class SomeType {
public static void SomeMethod(object[] values) {
foreach (var value in values) {
Console.WriteLine(value);
}
}
}
class Program {
static Action<T> CreateAction<T>() {
ParameterExpression parameter = Expression.Parameter(
typeof(T),
"parameter"
);
List<Expression> properties = new List<Expression>();
foreach (var info in typeof(T).GetProperties()) {
// can not handle indexers
if(info.GetIndexParameters().Length == 0) {
Expression property = Expression.Property(parameter, info);
properties.Add(Expression.Convert(property, typeof(object)));
}
}
Expression call = Expression.Call(
typeof(SomeType).GetMethod("SomeMethod"),
Expression.NewArrayInit(typeof(object), properties)
);
return Expression.Lambda<Action<T>>(call, parameter).Compile();
}
static void Main(string[] args) {
Customer c = new Customer();
c.Name = "Alice";
c.ID = 1;
CreateAction<Customer>()(c);
}
}
class Customer {
public string Name { get; set; }
public int ID { get; set; }
}
}
Of course this will be easier in .NET 4.0 with the LoopExpression.

Related

Build Expression tree to Add elements to Collection dynamically c#

I have a class and I need to iterate tru each property reading the attribute name to map to my data Source the value, in the cases where I have a ICollection that property will have multiple attributes to map the correct value.
I'm using Expression trees to set the values efficiently to each property but I'm having issues to set the values to the Collection.
I think this is because I need to create an instance of that Collection but I don't know. I'm a bit lost on that one here's what I got:
[AttributeUsage(AttributeTargets.Property, AllowMultiple = true)]
public class MapToAttribute : Attribute
{
public MapToAttribute(string field)
{
Field = field;
}
public string Field { get; private set; }
}
public class MyDataClass
{
[MapTo("one")]
public int propOne { get; set; }
[MapTo("two")]
public string propTwo { get; set; }
[MapTo("item1")]
[MapTo("item2")]
[MapTo("item3")]
public ICollection<int> collection { get; set; }
}
class Program
{
static void Main(string[] args)
{
var setter = SetValues<MyDataClass>();
}
private static IEnumerable<T> SetValues<T>()
where T : new()
{
var properties = GetClassProperties<T>();
var results = new List<T>();
for (int x = 1; x<=100; x++)
{
var row = new T();
//Simulated Datasource
var dataSource = new Dictionary<string, object>();
dataSource.Add("one", x);
dataSource.Add("two", x.ToString());
dataSource.Add("item1", x);
dataSource.Add("item2", x+x);
dataSource.Add("item3", x*x);
foreach (var property in properties)
{
//this line executes the Action
property.Value(row, dataSource[property.Key]);
}
results.Add(row);
}
return results;
}
private static Dictionary<string, Action<T, object>> GetClassProperties<T>()
{
var setters = new Dictionary<string, Action<T, object>>();
var instance = Expression.Parameter(typeof(T));
var argument = Expression.Parameter(typeof(object));
foreach (var property in typeof(T).GetProperties())
{
var names = property.GetCustomAttributes(typeof(MapToAttribute), true)
.Select(p => ((MapToAttribute)p).Field);
var setter = Expression.Lambda<Action<T, object>>(
Expression.Call(
instance,
property.GetSetMethod(),
Expression.Convert(argument, property.PropertyType)
), instance, argument
).Compile();
// Due to the types I cannot just assign a value to a ICollection,
// that's why I tried to create HERE a different setter
// when the property Type is ICollection, I commented out the code failing.
//var getCollection = Expression.Lambda<Func<T, object>>(
// Expression.Call(
// instance,
// prop.GetGetMethod()
// ), instance
// ).Compile();
//Action<T, object> setter = (classInstance, value) =>
// getCollection(classInstance).Add(value);
foreach (var name in names)
{
setters.Add(name, setter);
}
}
return setters;
}
}
First of all to make life easier you will need to initialize collection:
public ICollection<int> collection { get; set; } = new List<int>();
Second try this:
private static Dictionary<string, Action<T, object>> GetClassProperties<T>()
{
var setters = new Dictionary<string, Action<T, object>>();
var instance = Expression.Parameter(typeof(T));
var argument = Expression.Parameter(typeof(object));
foreach (var property in typeof(T).GetProperties())
{
var names = property.GetCustomAttributes(typeof(MapToAttribute), true)
.Select(p => ((MapToAttribute)p).Field)
.ToList();
if (property.PropertyType.IsGenericType) // start checking that prop implements ICollection
{
// get ICollection generic parameter type
var genericParam = property.PropertyType.GetGenericArguments().First();
// construct concrete ICollection type
var propColType = typeof(ICollection<>).MakeGenericType(genericParam);
if (propColType.IsAssignableFrom(property.PropertyType)) // check if is ICollection of genericParam
{
var getCollection = Expression.Call(instance, property.GetGetMethod());
var addMethod = propColType.GetMethod("Add");
var colAddSetter = Expression.Lambda<Action<T, object>>(
Expression.Call(getCollection, addMethod, Expression.Convert(argument, genericParam)),
instance, argument)
.Compile();
foreach (var name in names)
{
setters.Add(name, colAddSetter);
}
continue; // process next property
}
}
// process "ordinary" property
var setter = Expression.Lambda<Action<T, object>>(
Expression.Call(
instance,
property.GetSetMethod(),
Expression.Convert(argument, property.PropertyType)
), instance, argument
).Compile();
setters.Add(names.Single(), setter); // todo throw normal exception instead of Single
}
return setters;
}

How to write a generic mock which maps interface properties to key-value pairs in c# using moq

I want to write a method which creates mocks for any interface.
public T GetMock<T>(IDictionary<string, object> data) where T : class
I care only about property getters first. All getters should return values which are stored in the dictionary. Property name is a key in this dictionary. Following code illustrates intended usage:
public interface IFoo
{
string Property1 { get; }
int Property2 { get; }
DateTime Property3 { get; }
}
[Test]
public void TestY()
{
var data = new Dictionary<string, object>
{
{"Property1", "Hello"},
{"Property2", 5},
{"Property3", DateTime.Today}
};
var mock = GetMock<IFoo>(data);
Assert.AreEqual("Hello", mock.Property1);
Assert.AreEqual(5, mock.Property2);
Assert.AreEqual(DateTime.Today, mock.Property3);
}
The point is that I want to mock ANY interface. So my generic mock crreation looks like:
public T GetMock<T>(IDictionary<string, object> data) where T : class
{
var mock = new Mock<T>();
var type = typeof(T);
var properties = type.GetProperties();
foreach (var property in properties)
{
var attributeName = property.Name;
var parameter = Expression.Parameter(type);
var body = Expression.Property(parameter, attributeName);
var lambdaExpression = Expression.Lambda<Func<T, object>>(body, parameter);
Func<object> getter = () => data[attributeName];
mock.Setup(lambdaExpression).Returns(getter);
}
return mock.Object;
}
It should work but there is an issue with type conversion. The test fails with a message:
System.ArgumentException : Expression of type 'System.Int32' cannot be
used for return type 'System.Object'
I guess I am missing some conversion lambda. Any suggestions how to fix the problem?
Guess the only option is to use Reflection, because current version is 4.2, but still - there's no "Mock.Setup(Expression expr)" implementation, as stated Patrick.
So, here's my sample:
public static class ConfigFactory<T> where T : class {
static T cachedImplInstance;
public static T BuildConfigGroupWithReflection() {
if (cachedImplInstance == null) {
Type interfaceType = typeof(T);
MethodInfo setupGetMethodInfo = typeof(Mock<T>).GetMethod("SetupGet");
Mock<T> interfaceMock = new Mock<T>();
IDictionary<Type, MethodInfo> genericSetupGetMethodInfos = new Dictionary<Type, MethodInfo>();
IDictionary<Type, MethodInfo> specificReturnsMethodInfos = new Dictionary<Type, MethodInfo>();
if (setupGetMethodInfo != null)
foreach (PropertyInfo interfaceProperty in interfaceType.GetProperties()) {
string propertyName = interfaceProperty.Name;
Type propertyType = interfaceProperty.PropertyType;
ParameterExpression parameter = Expression.Parameter(interfaceType);
MemberExpression body = Expression.Property(parameter, propertyName);
var lambdaExpression = Expression.Lambda(body, parameter);
MethodInfo specificSetupGetMethodInfo =
genericSetupGetMethodInfos.ContainsKey(propertyType) ?
genericSetupGetMethodInfos[propertyType] :
genericSetupGetMethodInfos[propertyType] = setupGetMethodInfo.MakeGenericMethod(propertyType);
object setupResult = specificSetupGetMethodInfo.Invoke(interfaceMock, new[] { lambdaExpression });
MethodInfo returnsMethodInfo =
specificReturnsMethodInfos.ContainsKey(propertyType) ?
specificReturnsMethodInfos[propertyType] :
specificReturnsMethodInfos[propertyType] = setupResult.GetType().GetMethod("Returns", new[] { propertyType });
if (returnsMethodInfo != null)
returnsMethodInfo.Invoke(setupResult, new[] { Settings.Default[propertyName] });
}
cachedImplInstance = interfaceMock.Object;
}
return cachedImplInstance;
}
}
Notice line "returnsMethodInfo.Invoke(setupResult, new[] { Settings.Default[propertyName] });" - you may put your dictionnary here.
Say, we have interface:
public interface IConfig {
string StrVal { get; }
int IntVal { get; }
StringCollection StrsVal { get; }
string DbConnectionStr { get; }
string WebSvcUrl { get; }
}
Then, usage is as follows (assuming we have "Settings" of our project with corresponding Names/Types/Values):
IConfig cfg0 = ConfigFactory<IConfig>.BuildConfigGroupWithReflection();
This is a half answer, since I don't see any support in Moq for doing this. To get the correct Func, do the following:
// In your for loop from above...
var attributeName = property.Name;
var parameter = Expression.Parameter(type);
var body = Expression.Property(parameter, attributeName);
// Add this line to create the correct Func type
var func = typeof(Func<,>).MakeGenericType(typeof(T), property.PropertyType);
// Then use this Func to create the lambda
var lambdaExpression = Expression.Lambda(func, body, parameter);
The problem is that Setup doesn't have an overload that allows you to pass in a non-generic expression that represents a Func. In otherwords, this won't compile:
// Error: cannot convert from 'System.Linq.Expressions.LambdaExpression'
// to 'System.Linq.Expressions.Expression<System.Action<T>>'
mock.Setup(lambdaExpression);
So at this point you're stuck.
You could submit an issue (or pull request) to the Moq project, though I don't know if this application has a wide enough audience...

Converting a reflection method to a compiled lambda

I'm new to lambda. So excuse me if my question is simple.
I have a method that uses reflection to set a property on some types:
public void WriteId(object obj, int id) {
var type = obj.GetType();
var prop = type.GetProperties(BindingFlags.Public | BindingFlags.Instance)
.Where(p => p.CanRead && p.CanWrite)
.Where(p => p.Name == "Id")
.Where(p.PropertyType == typeof(int))
.FirstOrDefault();
if(prop != null)
prop.SetValue(obj, id, null);
}
Can you show me please how can I create a lambda that do same job? Actually I want to create a lambda for each type, compile it, and cache it. Thanks in advance.
I would install FastMember from NuGet, and then use:
var wrapped = ObjectAccessor.Create(obj);
obj["Id"] = id;
which does pretty much what you say, except it happens to use ILGenerator via TypeBuilder (rather than Expression) - but all the caching etc is there.
A second cheaky approach is to get dynamic to do it all for you:
((dynamic)obj).Id = id;
But if you want to use Expression for other reasons:
using System;
using System.Linq.Expressions;
static class Program
{
static void Main()
{
var obj = new Foo { Id = 2 };
WriteId(obj, 6);
Console.WriteLine(obj.Id); // 6
}
private static class SneakyCache<T>
{
public static readonly Action<T, int> SetId;
static SneakyCache()
{
var obj = Expression.Parameter(typeof(T), "obj");
var id = Expression.Parameter(typeof(int), "id");
SetId = Expression.Lambda<Action<T, int>>(
Expression.Assign(Expression.Property(obj, "Id"), id),
obj, id).Compile();
}
}
public static void WriteId<T>(T obj, int id) where T : class
{
SneakyCache<T>.SetId(obj, id);
}
}
class Foo
{
public int Id { get; set; }
}

Creating an performant open delegate for an property setter or getter

An open delegate is a delegate to an instance method without the target. To call it you supply the target as its first parameter. They are a clever way to optimize code that otherwise would use reflection and have poor performance. For an intro to open delegates see this. The way you would use it in practice is to have expensive reflection code to build these open delegates, but then you would be able to call them very cheaply as a simple Delegate call.
I'm trying to write code that will transform an arbitrary PropertyInfo, into such an delegate for its setter. So far I came up with this:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
namespace Test
{
class TestClass
{
static Action<T, object> MakeSetterDelegate<T>(PropertyInfo property)
{
MethodInfo setMethod = property.GetSetMethod();
if (setMethod != null && setMethod.GetParameters().Length == 1) //skips over nasty index properties
{
//To be able to bind to the delegate we have to create a delegate
//type like: Action<T,actualType> rather than Action<T,object>.
//We use reflection to do that
Type setterGenericType = typeof(Action<,>);
Type delegateType = setterGenericType.MakeGenericType(new Type[] { typeof(T), property.PropertyType });
var untypedDelegate = Delegate.CreateDelegate(delegateType, setMethod);
//we wrap the Action<T,actualType> delegate into an Action<T,object>
Action<T, object> setter = (instance, value) =>
{
untypedDelegate.DynamicInvoke(new object[] { instance, value });
};
return setter;
}
else
{
return null;
}
}
int TestProp
{
set
{
System.Diagnostics.Debug.WriteLine("Called set_TestProp");
}
}
static void Test()
{
PropertyInfo property = typeof(TestClass).GetProperty("TestProp");
Action<TestClass, object> setter = MakeSetterDelegate<TestClass>(property);
TestClass instance = new TestClass();
setter(instance, 5);
}
}
}
Similar code would be written for the getter. It works, but the setter delegate uses a DynamicInvoke to convert from an Action<derivedType> to Action<object>, which I suspect is eating a good part of the optimization I'm after. So the questions are:
Is the DynamicInvoke a real concern?
Is there anyway around it?
DynamicInvoke will not make a performant setter. Reflection against a generic inner type is your better option here, as this will allow you to use typed delegates. Another option is DynamicMethod, but then you need to worry about a few IL details.
You might want to look at HyperDescriptor, which wraps up the IL work into a PropertyDescriptor implementation. Another option is the Expression API (if you are using .NET 3.5 or above):
static Action<T, object> MakeSetterDelegate<T>(PropertyInfo property)
{
MethodInfo setMethod = property.GetSetMethod();
if (setMethod != null && setMethod.GetParameters().Length == 1)
{
var target = Expression.Parameter(typeof(T));
var value = Expression.Parameter(typeof(object));
var body = Expression.Call(target, setMethod,
Expression.Convert(value, property.PropertyType));
return Expression.Lambda<Action<T, object>>(body, target, value)
.Compile();
}
else
{
return null;
}
}
Or alternatively with a generic type:
abstract class Setter<T>
{
public abstract void Set(T obj, object value);
}
class Setter<TTarget, TValue> : Setter<TTarget>
{
private readonly Action<TTarget, TValue> del;
public Setter(MethodInfo method)
{
del = (Action<TTarget, TValue>)
Delegate.CreateDelegate(typeof(Action<TTarget, TValue>), method);
}
public override void Set(TTarget obj, object value)
{
del(obj, (TValue)value);
}
}
static Action<T, object> MakeSetterDelegate<T>(PropertyInfo property)
{
MethodInfo setMethod = property.GetSetMethod();
if (setMethod != null && setMethod.GetParameters().Length == 1)
{
Setter<T> untyped = (Setter<T>) Activator.CreateInstance(
typeof(Setter<,>).MakeGenericType(typeof(T),
property.PropertyType), setMethod);
return untyped.Set;
}
else
{
return null;
}
}
I once made this class. Perhaps it helps:
public class GetterSetter<EntityType,propType>
{
private readonly Func<EntityType, propType> getter;
private readonly Action<EntityType, propType> setter;
private readonly string propertyName;
private readonly Expression<Func<EntityType, propType>> propertyNameExpression;
public EntityType Entity { get; set; }
public GetterSetter(EntityType entity, Expression<Func<EntityType, propType>> property_NameExpression)
{
Entity = entity;
propertyName = GetPropertyName(property_NameExpression);
propertyNameExpression = property_NameExpression;
//Create Getter
getter = propertyNameExpression.Compile();
// Create Setter()
MethodInfo method = typeof (EntityType).GetProperty(propertyName).GetSetMethod();
setter = (Action<EntityType, propType>)
Delegate.CreateDelegate(typeof(Action<EntityType, propType>), method);
}
public propType Value
{
get
{
return getter(Entity);
}
set
{
setter(Entity, value);
}
}
protected string GetPropertyName(LambdaExpression _propertyNameExpression)
{
var lambda = _propertyNameExpression as LambdaExpression;
MemberExpression memberExpression;
if (lambda.Body is UnaryExpression)
{
var unaryExpression = lambda.Body as UnaryExpression;
memberExpression = unaryExpression.Operand as MemberExpression;
}
else
{
memberExpression = lambda.Body as MemberExpression;
}
var propertyInfo = memberExpression.Member as PropertyInfo;
return propertyInfo.Name;
}
test:
var gs = new GetterSetter<OnOffElement,bool>(new OnOffElement(), item => item.IsOn);
gs.Value = true;
var result = gs.Value;

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