Expression <tdelegate>子类的属性是否在C#?</tdelegate>中是内在的

时间:2015-03-26 13:58:14

标签: c# expression

我想知道编译期间Expression类及其子类(UnaryExpressionMemberExpression等)的属性是否折叠为内联常量对象?

例如在ASP.Net MVC中,视图模型的强类型属性名称(例如Html.LabelFor(vm => vm.FirstName))Html.LabelFor)的常见做法是期望一个表达式,然后它会检查{ {1}}等等。这些属性是在运行时评估的(使用运行时编译成本等)还是在编译期间进行了优化(如.Net 4.5中的Body.Member.Name)?

3 个答案:

答案 0 :(得分:2)

“评估”是什么意思?

它没有编译。所有必要的信息都已在编译期间准备好。所以从这个意义上讲,它并没有被多次“评估”。表达式没有运行时编译,这不是表达式树的含义(除非你明确地调用Compile,它基本上将表达式树转换为方法委托)。

但是,每次都创建表达式树

for (int i = 0; i < 1000000; i++)
{
  Expression<Func<DataTable, string>> e = t => t.TableName;
}

在循环的每一步中,表达式一次又一次地创建,即使它是可证明的常量(所有Expression都是不可变的,并且没有闭包)。

这不是 free ,但它也不是非常昂贵 - 这个示例代码每秒产生大约17万个表达式(这是一个非常愚蠢的基准,但对于球场估计并不是那么糟糕) )。

部分成本是必须创建的所有Expression个实例。有些是涉及的反思。更简单的表达式 更容易创建。

但是,您还需要考虑周围的成本:

  • 那么在UI的上下文中呢?成本被效益广泛抵消。
  • I / O?任何I / O操作旁边都可以忽略不计。
  • 在数百万个将两个数字加在一起的元素的循环中深入?不要:))

如果你发现它在某个地方(配置文件,配置文件,配置文件)导致瓶颈,你通常可以缓存表达式树 - 正如我所说,它们是不可变的,所以这是安全且容易的。实际上,您可以轻松地创建具有要在运行时替换参数的巨大表达式树 - 由于表达式的不变性,您只需要创建实际必须替换的表达式 - 其余的将保持不变。

对于ASP.NET MVC,您使用这样的方法可以轻松访问对象元数据。您在这里获得的巨大好处是对您引用的属性和字段进行编译时检查 - 这使得保持应用程序一致性变得容易得多,同时代码非常容易阅读(“它是FirstName属性的标签,duh。“)写。它将通过重构与您保持联系。

答案 1 :(得分:1)

您可以使用任何反编译器或linqpad查看反编译代码。它将创建一系列Expression.XXX调用以在运行时构建表达式。

例如,以下C#代码:

Expression<Func<string>> foo = () => "";

编译成:

IL_0000:  nop         
IL_0001:  ldstr       ""
IL_0006:  ldtoken     System.String
IL_000B:  call        System.Type.GetTypeFromHandle
IL_0010:  call        System.Linq.Expressions.Expression.Constant
IL_0015:  ldc.i4.0    
IL_0016:  newarr      System.Linq.Expressions.ParameterExpression
IL_001B:  call        System.Linq.Expressions.Expression.Lambda
IL_0020:  stloc.0     // foo
IL_0021:  ret    

答案 2 :(得分:1)

重新创建它们......即使在最简单的情况下,也会重新创建Expression

static Expression<Func<int, int>> otherFunc;

public static void MyMethod(Expression<Func<int, int>> func)
{
    if (otherFunc == null)
    {
        otherFunc = func;
        Console.WriteLine("First time");
    }
    else
    {
        Console.WriteLine("Same Expression<Func<int>>: {0}", object.ReferenceEquals(func, otherFunc));
        Console.WriteLine("Same Parameter: {0}", object.ReferenceEquals(func.Parameters[0], otherFunc.Parameters[0]));
    }
}

for (int i = 0; i < 2; i++)
{
    MyMethod(x => 5);
}

结果:

First time
Same Expression<Func<int>>: False
Same Parameter: False

我们在一个方法中,我们在MyMethod内调用for,而Expression每次都不同。