Expression.Coalesce的转换参数是什么?

时间:2018-12-03 06:42:45

标签: c# clr roslyn expression-trees

有关此问题的上下文,请参见the documentation for the Coalesce(Expression, Expression, LambdaExpression) overload of the Expression.Coalesce Method。我专门指的是此重载的第三个参数。我对此有一些疑问,无法在任何地方找到答案,包括Microsoft的文档:

  • 为什么会选择使用此重载来提供转化?
  • 在编译表达式时将如何使用它?
  • 如何构造LambdaExpression一遍(我只能假设特定的参数签名,并且期望返回值类型)?

我已经反复尝试(通过将使用代码中的??运算符的各种lambda函数转换为Expression<>)来使C#编译器为我编写一个使用此参数的表达式树。但是每次我使用调试器在结果树中检查the corollary property of the conversion parameter中带有NodeType Coalesce的表达式时,它就是null

之所以问,是因为我正在研究一个通过分析表达式树而工作的库,因此我需要它来正确理解和支持这些转换。

1 个答案:

答案 0 :(得分:2)

您可以通过查看源代码和C#规范来弄清楚C#编译器的作用。

如果您查看the code in the C# compiler that handles the coalesce expression for expression trees,则会注意到它仅在左侧子表达式包含用户定义的表达式时才使用conversion

然后您可以查看at the section The null coalescing operator of the C# spec看看何时发生:

  

具体来说,a ?? b的处理如下:

     
      
  • 如果A存在并且不是可为空的类型或引用类型,则将发生编译时错误。
  •   
  • […]
  •   
  • 否则,如果b具有类型B,并且存在从aB的隐式转换,则结果类型为B。在运行时,首先评估a。如果a不为空,则将a展开为类型A0(如果A存在并且可以为空)并转换为类型B,则它变为结果。否则,将评估b并成为结果。
  •   
  • […]
  •   

因此,我们需要类型A,该类型具有用户定义的向B的隐式转换,并在空合并表达式中使用这两种类型:

class A
{
    public static implicit operator B(A s) => null;
}

class B {}

…

Expression<Func<B>> e = () => new A() ?? new B();

如果您decompile this code, you'll see

NewExpression left = Expression.New(typeof(A));
NewExpression right = Expression.New(typeof(B));
ParameterExpression parameterExpression = Expression.Parameter(typeof(A), "p");
UnaryExpression body = Expression.Convert(
    parameterExpression, typeof(B),
    (MethodInfo)MethodBase.GetMethodFromHandle((RuntimeMethodHandle)/*OpCode not supported: LdMemberToken*/));
ParameterExpression[] obj = new ParameterExpression[1];
obj[0] = parameterExpression;
Expression.Lambda<Func<B>>(
    Expression.Coalesce(left, right, Expression.Lambda(body, obj)), Array.Empty<ParameterExpression>());

用反射代码替换GetMethodFromHandle以获得A.op_Implicit,并且您具有使用非空Coalesce创建有效的Conversion表达式树的代码。

相关问题