2013-05-21 3 views
0

Это длинная история): У меня есть несколько типов выглядеть следующим образом:Выражения для чтения от собственности и написать частное поле только для чтения

public class Model { 
    private readonly SomeType _member; 
    private readonly AnotherType _member2; 
    public Model(SomeType member, AnotherType member2) { 
     _member = member; 
     _member2 = member2; 
    } 
    public SomeType Member { get { return _member; } } 
    public AnotherType Member2 { get { return _member2; } } 
} 

Я пытаюсь создать несколько выражений для создания экземпляра класса , читает свойства из некоторых других объектов (обычно анонные объекты) и записывает значения в частные поля созданного экземпляра - на основе указанного соглашения об именах: Prop имеет имя поля: _prop.

Я имею в виду, я хочу написать ниже объектов нового экземпляра Model:

var anon1 = new { Member = "something" }; 
// expected: new Model with _member = "something" 

var anon2 = new { Member2 = "something" }; 
// expected: new Model with _member2 = "something" 

var anon3 = new { Member = "something", Member2 = "something else" }; 
// expected: new Model with _member = "something" and _member2 = "something else" 

Я создал ниже код, он создает новые экземпляры, но ничего не делать с полями. Можете ли вы помочь мне найти, где я ошиблась, пожалуйста?

public class InstanceCreator { 

    static public readonly Func<FieldInfo, PropertyInfo, bool> NamingConvention; 

    static InstanceCreator() { 
     NamingConvention = (f, p) => { 
      var startsWithUnderscope = f.Name.StartsWith("_"); 
      var hasSameName = p.Name.Equals(f.Name.Remove(0, 1), StringComparison.OrdinalIgnoreCase); 
      var hasSameType = f.FieldType == p.PropertyType; 
      return startsWithUnderscope && hasSameName && hasSameType && 
        f.IsInitOnly && !p.CanWrite; 
     }; 
    } 

    private readonly Type _type; 
    private readonly Func<dynamic, dynamic> _creator; 

    public InstanceCreator(Type type) { 
     _type = type; 
     var ctor = GetCtor(type); 
     var propertyToFieldWriters = MakeWriters(type); 
     _creator = MakeCreator(type, ctor, propertyToFieldWriters); 
    } 

    private Expression GetCtor(Type type) { 
     if (type == typeof(string)) // ctor for string 
      return Expression.Lambda<Func<dynamic>>(
       Expression.Constant(string.Empty)); 
     if (type.IsValueType || // type has a parameterless ctor 
      type.GetConstructor(Type.EmptyTypes) != null) 
      return Expression.Lambda<Func<dynamic>>(Expression.New(type)); 
     var info = typeof(FormatterServices).GetMethod("GetUninitializedObject"); 
     var call = Expression.Call(info, Expression.Constant(type)); 
     return call; 
     //return Expression.Lambda<Func<dynamic>>(call); 
    } 

    private IEnumerable<PropertyToFieldMapper> MakeWriters(Type type) { 
     var properties = type.GetProperties(BindingFlags.Instance | BindingFlags.Public); 
     var fields = type.GetFields(BindingFlags.Instance | BindingFlags.NonPublic); 
     var list = (from field in fields 
        let property = properties.FirstOrDefault(prop => NamingConvention(field, prop)) 
        where property != null 
        select new PropertyToFieldMapper(field, property)).ToList(); 
     foreach (var item in list) { 
      var sourceParameter = Expression.Parameter(type, "sourceParameter"); 
      var propertyGetter = Expression.Property(sourceParameter, item.Property.Name); 
      var targetParameter = Expression.Parameter(type, "targetParameter"); 
      var setterInfo = item.Field.GetType().GetMethod("SetValue", new[] { typeof(object), typeof(object) }); 
      var setterCall = Expression.Call(Expression.Constant(item.Field), setterInfo, 
       new Expression[] { 
            Expression.Convert(targetParameter,typeof(object)), 
            Expression.Convert(propertyGetter,typeof(object)) 
           }); 
      var call = Expression.Lambda(setterCall, new[] { targetParameter, sourceParameter }); 
      item.Call = call; 
     } 
     return list; 
    } 

    private Func<dynamic, dynamic> MakeCreator(
     Type type, Expression ctor, 
     IEnumerable<PropertyToFieldMapper> writers) { 

     var list = new List<Expression>(); 

     // creating new target 
     var targetVariable = Expression.Variable(type, "targetVariable"); 
     list.Add(Expression.Assign(targetVariable, Expression.Convert(ctor, type))); 

     // find all properties in incoming data 
     var sourceParameter = Expression.Parameter(typeof(object), "sourceParameter"); 
     var sourceTypeVariable = Expression.Variable(typeof(Type)); 
     var sourceTypeGetter = Expression.Call(sourceParameter, "GetType", Type.EmptyTypes); 
     list.Add(Expression.Assign(sourceTypeVariable, sourceTypeGetter)); 
     var sourcePropertiesVariable = Expression.Variable(typeof(PropertyInfo[])); 
     var sourcePropertiesGetter = Expression.Call(sourceTypeVariable, "GetProperties", Type.EmptyTypes); 
     list.Add(Expression.Assign(sourcePropertiesVariable, sourcePropertiesGetter)); 

     // itrate over writers and add their Call to block 
     foreach (var writer in writers) { 
      var param = Expression.Parameter(typeof(PropertyInfo)); 
      var prop = Expression.Property(param, "Name"); 
      var eq = Expression.Equal(Expression.Constant(writer.Property.Name), prop); 
      var any = CallAny.Call(sourcePropertiesVariable, Expression.Lambda(eq, param)); 
      var predicate = Expression.IfThen(any, 
       Expression.Lambda(writer.Call, new[] { targetVariable, sourceParameter })); 
      list.Add(predicate); 
     } 

     list.Add(targetVariable); 

     var block = Expression.Block(new[] { targetVariable, sourceTypeVariable, sourcePropertiesVariable }, list); 

     var lambda = Expression.Lambda<Func<dynamic, dynamic>>(
      block, new[] { sourceParameter } 
      ); 

     return lambda.Compile(); 
    } 

    public dynamic Create(dynamic data) { 
     return _creator.Invoke(data); 
    } 

    private class PropertyToFieldMapper { 

     private readonly FieldInfo _field; 
     private readonly PropertyInfo _property; 

     public PropertyToFieldMapper(FieldInfo field, PropertyInfo property) { 
      _field = field; 
      _property = property; 
     } 

     public FieldInfo Field { 
      get { return _field; } 
     } 

     public PropertyInfo Property { 
      get { return _property; } 
     } 

     public Expression Call { get; set; } 
    } 
} 

Кроме того, у меня есть этот CallAny класс, созданный из here.

public class CallAny { 

    public static Expression Call(Expression collection, Expression predicate) { 
     Type cType = GetIEnumerableImpl(collection.Type); 
     collection = Expression.Convert(collection, cType); 

     Type elemType = cType.GetGenericArguments()[0]; 
     Type predType = typeof(Func<,>).MakeGenericType(elemType, typeof(bool)); 

     // Enumerable.Any<T>(IEnumerable<T>, Func<T,bool>) 
     var anyMethod = (MethodInfo) 
      GetGenericMethod(typeof(Enumerable), "Any", new[] { elemType }, 
       new[] { cType, predType }, BindingFlags.Static); 

     return Expression.Call(anyMethod, collection, predicate); 
    } 

    static MethodBase GetGenericMethod(Type type, string name, Type[] typeArgs, Type[] argTypes, BindingFlags flags) { 
     int typeArity = typeArgs.Length; 
     var methods = type.GetMethods() 
      .Where(m => m.Name == name) 
      .Where(m => m.GetGenericArguments().Length == typeArity) 
      .Select(m => m.MakeGenericMethod(typeArgs)); 

     return Type.DefaultBinder.SelectMethod(flags, methods.ToArray(), argTypes, null); 
    } 

    static Type GetIEnumerableImpl(Type type) { 
     // Get IEnumerable implementation. Either type is IEnumerable<T> for some T, 
     // or it implements IEnumerable<T> for some T. We need to find the interface. 
     if (IsIEnumerable(type)) 
      return type; 
     Type[] t = type.FindInterfaces((m, o) => IsIEnumerable(m), null); 
     Debug.Assert(t.Length == 1); 
     return t[0]; 
    } 

    static bool IsIEnumerable(Type type) { 
     return type.IsGenericType 
      && type.GetGenericTypeDefinition() == typeof(IEnumerable<>); 
    } 
} 

А вот использование:

var inst = new InstanceCreator(typeof (Model)).Create(new {MyData}); 

ответ

0

Ну, я нашел проблему. Я должен использовать ExpandoObject вместо ключевого слова dynamic. И передайте его логике как IDictionary<string, object>. Вот решение:

public class InstanceCreator { 

    static public readonly Func<FieldInfo, PropertyInfo, bool> NamingConvention; 

    static InstanceCreator() { 
     NamingConvention = (f, p) => { 
      var startsWithUnderscope = f.Name.StartsWith("_"); 
      var hasSameName = p.Name.Equals(f.Name.Remove(0, 1), StringComparison.OrdinalIgnoreCase); 
      var hasSameType = f.FieldType == p.PropertyType; 
      return startsWithUnderscope && hasSameName && hasSameType && 
        f.IsInitOnly && !p.CanWrite; 
     }; 
    } 

    private readonly Type _type; 
    private readonly Func<IDictionary<string, object>, dynamic> _creator; 

    public InstanceCreator(Type type) { 
     _type = type; 
     var ctor = GetCtor(type); 
     var propertyToFieldMappers = MakeMappers(type); 
     _creator = MakeCreator(type, ctor, propertyToFieldMappers); 
    } 

    private Expression GetCtor(Type type) { 
     if (type == typeof(string)) // ctor for string 
      return Expression.Lambda<Func<dynamic>>(
       Expression.Constant(string.Empty)); 
     if (type.IsValueType || // type has a parameterless ctor 
      type.GetConstructor(Type.EmptyTypes) != null) 
      return Expression.Lambda<Func<dynamic>>(Expression.New(type)); 
     var info = typeof(FormatterServices).GetMethod("GetUninitializedObject"); 
     return Expression.Call(info, Expression.Constant(type)); 
    } 

    private IEnumerable<PropertyToFieldMapper> MakeMappers(Type type) { 
     var properties = type.GetProperties(BindingFlags.Instance | BindingFlags.Public); 
     var fields = type.GetFields(BindingFlags.Instance | BindingFlags.NonPublic); 
     var list = from field in fields 
        let property = properties.FirstOrDefault(prop => NamingConvention(field, prop)) 
        where property != null 
        select new PropertyToFieldMapper(field, property); 
     return list; 
    } 

    private Func<IDictionary<string, object>, dynamic> MakeCreator(
     Type type, Expression ctor, 
     IEnumerable<PropertyToFieldMapper> maps) { 

     var list = new List<Expression>(); 
     var vList = new List<ParameterExpression>(); 

     // creating new target 
     var targetVariable = Expression.Variable(type, "targetVariable"); 
     vList.Add(targetVariable); 
     list.Add(Expression.Assign(targetVariable, Expression.Convert(ctor, type))); 

     // accessing source 
     var sourceType = typeof(IDictionary<string, object>); 
     var sourceParameter = Expression.Parameter(sourceType, "sourceParameter"); 

     // calling source ContainsKey(string) method 
     var containsKeyMethodInfo = sourceType.GetMethod("ContainsKey", new[] { typeof(string) }); 

     var accessSourceIndexerProp = sourceType.GetProperty("Item"); 
     var accessSourceIndexerInfo = accessSourceIndexerProp.GetGetMethod(); 

     // itrate over writers and add their Call to block 
     var containsKeyMethodArgument = Expression.Variable(typeof(string), "containsKeyMethodArgument"); 
     vList.Add(containsKeyMethodArgument); 
     foreach (var map in maps) { 
      list.Add(Expression.Assign(containsKeyMethodArgument, Expression.Constant(map.Property.Name))); 
      var containsKeyMethodCall = Expression.Call(sourceParameter, containsKeyMethodInfo, 
                 new Expression[] { containsKeyMethodArgument }); 

      // creating writer 
      var sourceValue = Expression.Call(sourceParameter, accessSourceIndexerInfo, 
               new Expression[] { containsKeyMethodArgument }); 
      var setterInfo = map.Field.GetType().GetMethod("SetValue", new[] { typeof(object), typeof(object) }); 
      var setterCall = Expression.Call(Expression.Constant(map.Field), setterInfo, 
       new Expression[] { 
            Expression.Convert(targetVariable,typeof(object)), 
            Expression.Convert(sourceValue,typeof(object)) 
           }); 
      list.Add(Expression.IfThen(containsKeyMethodCall, setterCall)); 
     } 
     list.Add(targetVariable); 

     var block = Expression.Block(vList, list); 

     var lambda = Expression.Lambda<Func<IDictionary<string, object>, dynamic>>(
      block, new[] { sourceParameter } 
      ); 

     return lambda.Compile(); 
    } 

    public dynamic Create(IDictionary<string, object> data) { 
     return _creator.Invoke(data); 
    } 

    private class PropertyToFieldMapper { 

     private readonly FieldInfo _field; 
     private readonly PropertyInfo _property; 

     public PropertyToFieldMapper(FieldInfo field, PropertyInfo property) { 
      _field = field; 
      _property = property; 
     } 

     public FieldInfo Field { 
      get { return _field; } 
     } 

     public PropertyInfo Property { 
      get { return _property; } 
     } 
    } 
} 
Смежные вопросы