【问题标题】:Why this Lambda Expression is casted as MemberExpression and UnaryExpression?为什么这个 Lambda 表达式被转换为 MemberExpression 和 UnaryExpression?
【发布时间】:2016-03-27 02:16:41
【问题描述】:

我是 c# 和 lambda 表达式的新手;在this 教程中,我无法理解代码对这个 Lambda 表达式的作用:

public ViewModel()
{
    base.AddRule(() => Aid, () =>
    Aid.Length >= (5 * 2) &&
    Aid.Length <= (16 * 2) &&
    Aid.Length % 2 == 0, "Invalid AID.");
}

这是AddRule 方法,教程说它将规则添加到规则字典中:

public void AddRule<T>(Expression<Func<T>> expression, Func<bool> ruleDelegate, string errorMessage)
{
    var name = GetPropertyName(expression);

    ruleMap.Add(name, new Binder(ruleDelegate, errorMessage));
}

protected static string GetPropertyName<T>(Expression<Func<T>> expression)
        {
            if (expression == null)
                throw new ArgumentNullException("expression");

            Expression body = expression.Body;
            MemberExpression memberExpression = body as MemberExpression;
            if (memberExpression == null)
            {
                memberExpression = (MemberExpression)((UnaryExpression)body).Operand;
            }
            return memberExpression.Member.Name;
        }

    }

() =&gt; Aid 是什么意思,为什么addrule 接收它并将其转换为 UnaryExpression 和 MemberExpression?

【问题讨论】:

    标签: c# lambda


    【解决方案1】:

    MemberExpression 是实例上的表达式或 MemberInfo 的静态表达式。

    UnaryExpression 是一个围绕目标和单个运算符的表达式(参见术语一元的定义)。这种运算符的一个很好的例子是演员表。另一种是对值类型进行装箱或拆箱的操作。

    那么为什么获取 MemberInfo 的示例需要处理 UnaryExpression?

    考虑

    public class Foo
    {
        public String RefType { get; set; }
        public int ValueType { get; set; }
    }
    

    还有一些代码

    Expression<Func<Foo, object>> getter;
    getter = f => f.RefType // compiler emits MemberExpression for RefType on a Foo instance
    getter = f => f.ValueType // compiler emits an UnaryExpression to Box a MemberExpression for ValueType on a Foo instance.
    

    【讨论】:

      【解决方案2】:

      () =&gt; Aid() =&gt; { return Aid; } 的简写,它返回一个属性(或字段——你没有显示它的声明)。通常这会产生一个匿名函数。

      但是因为AddRule 方法采用Expression&lt;Func&lt;T&gt;&gt; 而不是Func&lt;T&gt;,所以编译器会创建创建AST(抽象语法树)而不是匿名函数的指令。 AST 可以编译成方法,但这里只用于提取属性/字段的名称。

      另一种方法是将属性名称作为字符串传递。使用成员表达式的优点是它易于重构:重命名属性也会更新此代码,确保规则仍然与正确的属性相关联。

      这是实际传递给 AddRule 的内容(您可以手动执行此操作,但您会失去重构友好的好处 - 此外,() =&gt; Aid 更简洁):

      Expression.Lambda<Func<T>>(
          Expression.Property(
              Expression.Constant(this),
              "Aid"));
      

      【讨论】:

      • 因此,GetPropertyName 在本例中仅返回属性名称“Aid”;对吗?
      • 没错。请注意,C# 6 引入了 nameof 运算符,这使这一切变得更加容易:编译器会将 nameof(Aid) 转换为 "Aid",从而为您提供相同的好处,而无需检查表达式。
      【解决方案3】:

      Aid 是该示例中使用的视图模型的属性:

          public string Aid
          {
              get { return Get(() => Aid); }
              set { Set(() => Aid, value); }
          }
      

      如果你从那篇文章下载了整个项目,你可以检查一下(链接在顶部)

      当您将属性传递给 lambda 表达式时,它将由 MemberExpression 表示。此类专用于此,如MemberExpression Class

      中所述

      【讨论】:

        猜你喜欢
        • 2020-12-02
        • 1970-01-01
        • 2013-09-07
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-01-15
        • 1970-01-01
        相关资源
        最近更新 更多