表达式树如何提供对局部变量的访问?

时间:2017-05-21 07:11:50

标签: c# linq

这个问题与闭包的工作方式无关。这个问题是关于LINQ如何决定引用运行时可解析表达式的内容,以及评估和放入该表达式的内容。

这个问题试图理解LINQ如何工作,用另一种语言实现类似的东西。

考虑以下LINQ查询,将其转换为表达式树:

var my_variable = "abc";
var qry = from x in source.Foo
          where x.SomeProp == my_variable
          select x.Bar;

由编译器映射到代码中:

var qry = source.Foo
           .Where(x => x.SomeProp == my_variable)
           .Select(x => x.Bar);

当它转换为表达式树时,LINQ如何知道引用表达式的内容,以及评估和将结果放入表达式的内容?

例如,如何评估my_variable并将结果放入expressino,但将x.SomeProp==转换为LINQ表达式树的部分?

C#编译器是否有一个硬编码的特殊列表,其中引用了LINQ的表达式? (即可以转换为SQL的最外层操作,包括:==,!=,&&,||,<,>,< =,> =,substring等)

2 个答案:

答案 0 :(得分:0)

只是通过检查明确表示.SomeProp依赖于x而x在表达式树解析时不是确定性的,因为它是先前函数的输出(其中)。

my_variable不是表达式树中任何表达式的输出,因此它的值可能在表达式树解析时已知,但很可能不会在表达式树中解析。即使它是已知的,因为这会阻止编译的表达式树被重用,因此它只会被视为表达式树评估中的输入值。

我没有反编译linq,但您可以考虑以下表达式树;

ExpressionTree myEx = new ExpressionTree(
   new MultiplicationExpression(
      new InputVariableExpression("@MyInputVar"),
      new ConstantExpression(22)
   )
);

评估你可以打电话

Dictionary<string, object> inputVars = new Dictionary<string, object>();
inputVars.Add("@MyInputVar",16);
int result = myEx.Evaluate(inputVars);

解析器可能会选择烘焙常量表达式,因为它知道&#39;它不能改变,但要考虑以下几点;

ExpressionTree anotherEx = new ExpressionTree(
   new AdditionExpression(
      myEx,
      new InputVariableExpression("@MyNextInputVar")
   )
);

这在概念上类似于在Linq x =>中使用替换变量,其中myEx是存储的表达式树,但实际上不是表达式的结果。在执行时间之前,表达式解析器无法独立地知道myEx的值。

Dictionary<string, object> inputVars = new Dictionary<string, object>();
inputVars.Add("@MyInputVar",16);
inputVars.Add("@MyNextInputVar",45);
int result = anotherEx.Evaluate(inputVars);

因此,在评估myEx期间,此执行代码将固有地评估anotherEx。如果myEx中只包含ConstantExpression元素,则可能只会对其进行一次评估并缓存结果,但由于它包含范围超出范围InputVariableExpression,因此清除了一个评估结果不能缓存以供后续使用。

答案 1 :(得分:-1)

这很容易测试:

int i = 1;
Func<int> func = () => i;
i = 2;
Console.WriteLine(func.Invoke());

打印出2,它告诉我们它存储符号,并且在评估函数之前不会对其进行评估。