【问题标题】:Programmatically Create Collection of Property Accessor Functions以编程方式创建属性访问器函数的集合
【发布时间】:2012-01-28 01:31:20
【问题描述】:

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

【问题讨论】:

标签: c#


【解决方案1】:

您可以使用反射来提取属性和表达式树,以帮助构建针对属性 getter 的委托:

                // 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 以帮助您入门(提示:递归)。最后一个指针:注意对象图中的循环!

【讨论】:

  • 另一个注意事项是注意嵌套对象中的null。如果b 为空,您期望a.b.c 返回什么?
  • 感谢@Ani 的回复。不过,对我来说最麻烦的是递归部分。我是表达式树的新手,不太了解如何递归,以便我构建将访问更深层属性的表达式。您是否有任何示例或指向更多信息的链接?此外,为了在这种情况下再投入一件事,一些属性可能是索引数组。我该如何解决这个问题?
  • let?这是 C#...?
【解决方案2】:
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;
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-07-10
    • 2022-01-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多