2012-01-27 46 views
4

我想要做的是采用任何类类型并为对象图中的所有属性创建一个'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 
} 
+0

看看Jon Skeet的这篇博文 - https://msmvps.com/blogs/jon_skeet/archive/2008/08/09/making-reflection-fly-and-exploring-delegates.aspx,它很漂亮除了递归之外,所有这些都很重要。 – 2012-01-27 16:57:32

+0

你可能也想看到这个http://stackoverflow.com/questions/16578857/using-nested-collections-of-lambda-expressions-to-create-an-object-graph – nawfal 2013-06-02 15:46:01

+0

乔恩的MVP博客不见了,这是一个他的(个人?)博客上的帖子副本: https://codeblog.jonskeet.uk/2008/08/09/making-reflection-fly-and-exploring-delegates/ – 2016-04-14 21:49:31

回答

5

您可以使用反射提取属性和表达式树来帮助构建委托对象纳克属性干将:

   // 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(); 

注意,虽然Delegate.CreateDelegate似乎是一个显而易见的选择,你将有一些问题,拳击值类型属性。表情树优雅地回避了这个问题。

请注意,您需要做更多的工作才能够获得“嵌套”属性,但希望我已经给了你足够的ypu来启动(提示:递归)。最后一个指针:注意对象图中的循环!

+0

另一个考虑是要小心' null“嵌套对象。如果'b'为空,你期望'a.b.c'返回什么? – Gabe 2012-01-27 17:34:30

+0

感谢您的回复,@Ani。不过,这是递归部分,对我来说证明是最麻烦的。我是表达式树的新手,不太了解如何缓解这种情况,以便构建可访问更深层属性的表达式。你有没有更多信息的例子或链接? 另外,为了将更多的事情放入情况中,一些属性可能是索引数组。我将如何解决这个问题? – cachance7 2012-01-27 22:01:10

+0

'let'?这是C#...吗? – 2014-08-14 12:17:25

0
public static List<Func<T, object>> CreateAccessors<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) { 
      var p = prop; 
      accessors.Add(x => p.GetValue(x, null)); 
     } 
    } 
    return accessors; 
} 

编辑:

这里是返回编译表达式应该比使用反射

前一个更快的变体
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; 
} 
相关问题