【问题标题】:Static(expression) alternative for type.GetFields()静态(表达式)替代 type.GetFields()
【发布时间】:2016-12-22 22:18:24
【问题描述】:

我正在尝试为类中的所有字段实现静态反射。 换句话说,我必须使用名称为所有这些字段创建 get 和 set。

我使用Set field value with Expression tree上的答案,得出以下解决方案

  public class ExpressionSetterGetter
    {
        public class SetterGetter<T> where T : class
        {
            public Delegate getter;
            public Action<T, object> setter;
        }

        public static Dictionary<string, SetterGetter<T>> GetFieldSetterGetterExpressions<T>() where T : class
        {

            var dic = new Dictionary<string, SetterGetter<T>>();
            SetterGetter<T> setterGetter;

            var type = typeof(T);
            var fields = type.GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
            foreach (var fieldInfo in fields)
            {
                var targetExp = Expression.Parameter(type, "target");
                var valueExp = Expression.Parameter(typeof(object), "value");
                var fieldExp = Expression.Field(targetExp, fieldInfo);
                var assignExp = Expression.Assign(fieldExp, Expression.Convert(valueExp, fieldExp.Type));
                var fieldSetter = Expression.Lambda<Action<T, object>>(assignExp, targetExp, valueExp).Compile();

                ParameterExpression objParm = Expression.Parameter(type, "obj");
                MemberExpression fieldExpr = Expression.Field(objParm, fieldInfo.Name);
                var fieldExprConverted = Expression.Convert(fieldExpr, typeof(object));
                var fieldGetter = Expression.Lambda(fieldExprConverted, objParm).Compile();

                setterGetter = new SetterGetter<T>() { setter = fieldSetter, getter = fieldGetter };

                dic.Add(fieldInfo.Name, setterGetter);
            }
            return dic;
        }
    }

现在,我有这两个问题。

  1. 我使用 type.GetFields(),但 MSDN 说它是一种反射方法。 这意味着编译器在运行时之前不知道类型。 我对吗?如果正确,使用的原因是什么 表达树。据我所知,表达式树转换为 编译时的代码,这意味着几乎没有额外的成本。

  2. 同样的逻辑。如果我将名称为的列表作为参数 字段必须包装的内容。换句话说,而不是 type.GetFields() 我只是把字段的名称作为参数。

    public static Dictionary&lt;string, SetterGetter&lt;T&gt;&gt; GetFieldSetterGetterExpressions&lt;T&gt;(IEnumerable&lt;string&gt; fieldNames)

显然,该列表在编译时是未知的。同样,CLR 将如何反应?

【问题讨论】:

  • CLR 不知道表达式树是什么。那只是一个任何人都可以编写的库。

标签: c# reflection expression clr


【解决方案1】:
  1. 表达式树的输出在编译时是已知的,它们是在运行时编译的,这是不同的。使用(缓存的)表达式树编译的优点是,在表达式构造和编译期间,反射繁重的工作只执行一次。从那里开始,调用生成的委托非常快。
  2. 如果您确定该列表仅指定了类中实际存在的字段并且您只提供了所有字段的子集,那么理论上您可以在一定程度上降低反射对性能的影响。但是您应该记住,您仍然需要每个实例的 FieldInfo 实例,这些实例只能使用反射来检索。因此,为每个已知字段调用 GetField 的总成本可能会高于为所有字段调用 GetFields

在编译时设置字段值的最简单方法是将私有字段转换为公共属性。然后,您可以随意设置它们。

这不是在开玩笑,而是对差异的解释——C# 不允许从外部设置字段,除非使用反射 API。这些 API 是为运行时设计的,而不是为编译时设计的。

如果您想改变规则并在编译时(准确地说,在编译后时)生成这样的代码,您应该使用一些第三方库,例如 PostSharpFody

这就是故事的结尾,只要您在当前版本或以前的版本之一中使用 C#。

【讨论】:

  • 感谢您的回答。你知道一本书我可以在哪里阅读如何在 C# 中构建表达式和(也许)其他功能性的东西?尽管它仍然比反射好得多,但我不得不询问在编译时执行它的可能性。假设我了解该类的所有信息,例如,我希望为所有打开的字段提供动态委托,如上所述。编译器在编译时不这样做的原因是什么?是否存在 CLR 限制或背后的某些逻辑?
  • @SimonS 我在回答中添加了更多解释
猜你喜欢
  • 2010-09-16
  • 2017-10-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-10-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多