C# 以编程方式创建属性访问器函数的集合

C# 以编程方式创建属性访问器函数的集合,c#,C#,我想做的是获取任何类类型并创建一个对象图中所有属性的“get”访问器列表 集合的确切格式、顺序等并不重要,我只是不知道如何开始识别和创建所有属性的访问器。它可能采取如下形式: public static List<Func<T,object>> CreateAccessors<T>() { Type t = typeof(T); // Identify all properties and properties of properties (etc

我想做的是获取任何类类型并创建一个对象图中所有属性的“get”访问器列表

集合的确切格式、顺序等并不重要,我只是不知道如何开始识别和创建所有属性的访问器。它可能采取如下形式:

public static List<Func<T,object>> CreateAccessors<T>()
{
   Type t = typeof(T);
   // Identify all properties and properties of properties (etc.) of T
   // Return list of lambda functions to access each one given an instance of T
}

public void MyTest()
{
   MyClass object1;
   var accessors = CreateAccessors<MyClass>();
   var myVal1 = accessors[0](object1);
   var myVal2 = accessors[1](object1);

   // myVal1 might now contain the value of object1.Property1
   // myVal2 might now contain the value of object1.Property4.ThirdValue.Alpha
}
公共静态列表CreateAccessors()
{
类型t=类型(t);
//确定T的所有属性和属性(等)的属性
//返回lambda函数列表,以访问给定T实例的每个函数
}
公共无效MyTest()
{
MyClass对象1;
var accessors=CreateAccessors();
var myVal1=accessors[0](object1);
var myVal2=访问器[1](object1);
//myVal1现在可能包含object1.Property1的值
//myVal2现在可能包含object1.Property4.ThirdValue.Alpha的值
}
公共静态列表CreateAccessors()
{
var访问器=新列表();
类型t=类型(t);
foreach(t.GetProperties(BindingFlags.Instance | BindingFlags.Public)中的PropertyInfo属性){
如果(道具可阅读){
var p=道具;
Add(x=>p.GetValue(x,null));
}
}
返回访问器;
}

编辑:

这里有一个变量,它返回编译后的表达式,应该比使用反射的前一个变量快得多

public static List<Func<T, object>> CreateAccessorsCompiled<T>()
{
    var accessors = new List<Func<T, object>>();
    Type t = typeof(T);
    foreach (PropertyInfo prop in t.GetProperties(BindingFlags.Instance | BindingFlags.Public)) {
        if (prop.CanRead) {
            ParameterExpression lambdaParam = Expression.Parameter(t, "instance");
            Expression bodyExpression;
            MemberExpression memberAccessExpression = Expression.MakeMemberAccess(Expression.Convert(lambdaParam, t), prop);
            if (prop.PropertyType == typeof(object)) {  // Create lambda expression:  (instance) => ((T)instance).Member
                bodyExpression = memberAccessExpression;
            } else { // Create lambda expression:  (instance) => (object)((T)instance).Member
                bodyExpression = Expression.Convert(memberAccessExpression, typeof(object));
            }
            var lambda = Expression.Lambda<Func<T, object>>(bodyExpression, lambdaParam);
            accessors.Add(lambda.Compile());
        }
    }
    return accessors;
}
公共静态列表CreateAccessorsCompiled() { var访问器=新列表(); 类型t=类型(t); foreach(t.GetProperties(BindingFlags.Instance | BindingFlags.Public)中的PropertyInfo属性){ 如果(道具可阅读){ ParameterExpression lambdaParam=Expression.Parameter(t,“实例”); 表达体表达; MemberExpression memberAccessExpression=Expression.MakeMemberAccess(Expression.Convert(lambdaParam,t),prop); if(prop.PropertyType==typeof(object)){//Create lambda expression:(instance)=>((T)instance).Member bodyExpression=memberAccessExpression; }else{//创建lambda表达式:(实例)=>(对象)((T)实例).Member bodyExpression=Expression.Convert(memberAccessExpression,typeof(object)); } var lambda=Expression.lambda(bodyExpression,lambdaParam); Add(lambda.Compile()); } } 返回访问器; }
您可以使用反射来提取属性和表达式树,以帮助构建以属性获取程序为目标的委托:

                // Start with all public instance properties of type
var accessors = from property in type.GetProperties
                         (BindingFlags.Instance | BindingFlags.Public)

                // Property must be readable
                where property.CanRead

                //Assemble expression tree of the form:
                // foo => (object) foo.Property

                // foo
                let parameter = Expression.Parameter(type, "foo")

                // foo.Property 
                let propertyEx = Expression.Property(parameter, property)

                // (object)foo.Property - We need this conversion
                // because the property may be a value-type.
                let body = Expression.Convert(propertyEx, typeof(object))

                // foo => (object) foo.Property
                let expr = Expression.Lambda<Func<T,object>>(body, parameter)

                // Compile tree to Func<T,object> delegate
                select expr.Compile();

return accessors.ToList();
//从类型为的所有公共实例属性开始
var accessors=type.GetProperties中的from属性
(BindingFlags.Instance | BindingFlags.Public)
//属性必须可读
where property.CanRead
//组装表单的表达式树:
//foo=>(对象)foo.Property
//福
let parameter=Expression.parameter(类型“foo”)
//食物财产
让propertyEx=Expression.Property(参数,属性)
//(object)foo.Property-我们需要此转换
//因为属性可能是值类型。
let body=Expression.Convert(propertyEx,typeof(object))
//foo=>(对象)foo.Property
设expr=Expression.Lambda(主体,参数)
//将树编译为Func委托
选择expr.Compile();
返回访问器。ToList();
请注意,尽管
Delegate.CreateDelegate
似乎是一个明显的选择,但装箱值类型属性时会遇到一些问题。表达式树巧妙地回避了这个问题


请注意,您还需要做更多的工作才能获得“嵌套”属性,但希望我已经提供了足够的ypu让您开始(提示:recurse)。最后一点:注意对象图中的循环

看一看Jon Skeet的这篇博文-,除了递归之外,它几乎都在那里。你可能想看到Jon的MVP博客也不见了,这是他(个人?)博客上的一篇博文:另一个需要注意的是嵌套对象中的
null
。如果
b
为空,您希望
a.b.c
返回什么?谢谢您的回复,@Ani。不过,递归部分对我来说最麻烦。我是表达式树的新手,不太了解如何递归,因此我正在构建能够访问更深层属性的表达式。你有更多信息的例子或链接吗?另外,在这种情况下,有些属性可能是索引数组。我如何解决这个问题?
let
?这是C#。。。?
                // Start with all public instance properties of type
var accessors = from property in type.GetProperties
                         (BindingFlags.Instance | BindingFlags.Public)

                // Property must be readable
                where property.CanRead

                //Assemble expression tree of the form:
                // foo => (object) foo.Property

                // foo
                let parameter = Expression.Parameter(type, "foo")

                // foo.Property 
                let propertyEx = Expression.Property(parameter, property)

                // (object)foo.Property - We need this conversion
                // because the property may be a value-type.
                let body = Expression.Convert(propertyEx, typeof(object))

                // foo => (object) foo.Property
                let expr = Expression.Lambda<Func<T,object>>(body, parameter)

                // Compile tree to Func<T,object> delegate
                select expr.Compile();

return accessors.ToList();