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

这个问题与闭包的工作方式无关。 这个问题是关于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等)

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

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

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

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

要评估你可能会打电话

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

解析器可能会选择在常量表达式中进行烘焙,因为它“知道”它不能更改,但请考虑以下内容;

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

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

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

因此,此执行代码将在评估myEx期间内在地评估anotherEx 。 如果myEx中只有ConstantExpression元素,它可能只被评估一次并且结果被缓存,但是因为它包含一个超出范围的InputVariableExpression它很清楚,一次评估的结果不能被高速缓存以供后续使用。

这很容易测试:

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

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