从属性读取并写入私有只读字段的表达式 [英] Expression for read from property and write to private readonly field

查看:136
本文介绍了从属性读取并写入私有只读字段的表达式的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这是一个很长的故事:我有一些类似的样子:

  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; }}
}

我正在尝试创建一些表达式来创建一个类的实例,从一些其他对象(通常为anon对象)中读取属性,并根据显示的命名约定将值写入创建的实例的私有字段: Prop具有字段名称:_prop



我的意思是我想把下面的对象写成一个新的模型

  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 =somethingand _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;
私有只读Func<动态,动态> _creator;

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

private表达式GetCtor(Type type){
if(type == typeof(string))// ctor for string
返回Expression.Lambda&FunC< ;动态>>(
Expression.Constant(string.Empty));
if(type.IsValueType || // type有一个无参数的ctor
type.GetConstructor(Type.EmptyTypes)!= null)
return Expression.Lambda&FunC< dynamic> Expression.New(类型));
var info = typeof(FormatterServices).GetMethod(GetUninitializedObject);
var call = Expression.Call(info,Expression.Constant(type));
return call;
//返回Expression.Lambda< Func<动态>>(调用);
}

private IEnumerable< PropertyToFieldMapper> MakeWriters(Type type){
var properties = type.GetProperties(BindingFlags.Instance | BindingFlags.Public);
var fields = type.GetFields(BindingFlags.Instance | BindingFlags.NonPublic);
var list =(从字段中的字段
let property = properties.FirstOrDefault(prop => NamingConvention(field,prop))
其中property!= null
选择新的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;
}
返回列表;
}

private Func<动态,动态> MakeCreator(
类型,表达式ctor,
IEnumerable< PropertyToFieldMapper> writers){

var list = new List< Expression>();

//创建新目标
var targetVariable = Expression.Variable(type,targetVariable);
list.Add(Expression.Assign(targetVariable,Expression.Convert(ctor,type)));

//查找传入数据中的所有属性
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));

//通过写入器添加他们的调用块
foreach(写入器中的var writer){
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创建(动态数据){
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;组; }
}
}

此外,我有这个 CallAny 类,由此处创建。


$ b $公共类CallAny {

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

类型elemType = cType.GetGenericArguments()[0];
键入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));

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

static类型GetIEnumerableImpl(Type type){
//获取IEnumerable实现。任何一种类型是IEnumerable< T>对于某些T,
//或者它实现IEnumerable< T>对于某些T.我们需要找到界面。
if(IsIEnumerable(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}); 


解决方案

嗯,我发现了这个问题。我应该使用 ExpandoObject 而不是 dynamic 关键字。并将其传递给逻辑为 IDictionary< string,object> 。这是解决方案:

  public class InstanceCreator {

static public readonly Func< FieldInfo,PropertyInfo,布尔> 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>,动态> _creator;

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

private表达式GetCtor(Type type){
if(type == typeof(string))// ctor for string
返回Expression.Lambda&FunC< ;动态>>(
Expression.Constant(string.Empty));
if(type.IsValueType || // type有一个无参数的ctor
type.GetConstructor(Type.EmptyTypes)!= null)
return Expression.Lambda&FunC< dynamic> Expression.New(类型));
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 =从字段中的字段
let property = properties.FirstOrDefault(prop => NamingConvention(field,prop))
其中property!= null
select new PropertyToFieldMapper字段,属性);
返回列表;
}

private Func< IDictionary< string,object>,动态> MakeCreator(
类型,表达式ctor,
IEnumerable< PropertyToFieldMapper> maps){

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

//创建新目标
var targetVariable = Expression.Variable(type,targetVariable);
vList.Add(targetVariable);
list.Add(Expression.Assign(targetVariable,Expression.Convert(ctor,type)));

//访问源
var sourceType = typeof(IDictionary< string,object>);
var sourceParameter = Expression.Parameter(sourceType,sourceParameter);

//调用源ContainsKey(string)方法
var containsKeyMethodInfo = sourceType.GetMethod(ContainsKey,new [] {typeof(string)});

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

// itrate over writers并将其调用添加到块
var containsKeyMethodArgument = Expression.Variable(typeof(string),containsKeyMethodArgument);
vList.Add(containsKeyMethodArgument);
foreach(map中的var map){
list.Add(Expression.Assign(containsKeyMethodArgument,Expression.Constant(map.Property.Name)));
var containsKeyMethodCall = Expression.Call(sourceParameter,containsKeyMethodInfo,
new Expression [] {containsKeyMethodArgument});

//创建作者
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>,动态>>(
块,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; }
}
}
}


It is a long story ): I have some types look like this:

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; } }
}

I'm trying to create some expressions to create an instance of class, reads properties from some other objects (anon objects usually), and write the values to created instance's private fields -based on shown naming convention: Prop has field name: _prop.

I mean I want to write below objects to a new instance of 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"

I have created below code, it creates new instances, but do nothing with fields. Can you help me to find where I did wrong please?

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; }
    }
}

Also, I have this CallAny class, created from 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<>);
    }
}

And here is usage:

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

解决方案

Well, I found the problem. I should use ExpandoObject instead of dynamic keyword. And pass it to logic as IDictionary<string, object>. Here is the solution:

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; }
        }
    }
}

这篇关于从属性读取并写入私有只读字段的表达式的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆