Expression.Coalesce的转换参数有什么用?

Dan*_*nry 5 c# clr expression-trees roslyn

有关此问题的上下文,请参阅Expression.Coalesce Method 的 Coalesce(Expression, Expression, LambdaExpression) 重载的文档。我特意指的是这个重载的第三个参数。我有一些关于它的问题,但我无法在任何地方找到答案,包括微软的文档:

  • 为什么要选择使用这种重载来提供转换?
  • 编译表达式时如何使用它?
  • 应该如何LambdaExpression构造一个传递(我只能假设一个特定的参数签名和预期的返回值类型)?

??我多次尝试(通过将代码中使用运算符的各种 lambda 函数转换为Expression<>)让 C# 编译器为我构建一个使用此参数的表达式树。但每次我使用调试器检查结果树中表达式 with 的转换参数的推论属性时,它都是.NodeType Coalescenull

我这么问是因为我正在开发一个通过分析表达式树来工作的库,我需要它来正确理解和支持这些转换。

svi*_*ick 5

您可以通过查看其源代码和 C# 规范来了解 C# 编译器的作用。

\n

如果您查看C# 编译器中处理表达式树的合并表达式的代码,您会注意到它仅conversion在左子表达式包含用户定义的表达式时使用。

\n

然后,您可以查看C# 规范的空合并运算符部分来查看何时发生这种情况:

\n
\n

具体a ?? b处理如下:

\n
    \n
  • 如果A存在并且不是可为 null 的类型或引用类型,则会发生编译时错误。
  • \n
  • [\xe2\x80\xa6]
  • \n
  • 否则,如果b有一个类型并且存在从到 的B隐式转换,则结果类型为。在运行时,首先评估。如果不为 null,则解包为 type (如果存在且可为空)并转换为 type ,这将成为结果。否则,进行评估并成为结果。aBBaaaA0ABb
  • \n
  • [\xe2\x80\xa6]
  • \n
\n
\n

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

\n
class A\n{\n    public static implicit operator B(A s) => null;\n}\n\nclass B {}\n\n\xe2\x80\xa6\n\nExpression<Func<B>> e = () => new A() ?? new B();\n
Run Code Online (Sandbox Code Playgroud)\n

如果你反编译这段代码,你会看到

\n
NewExpression left = Expression.New(typeof(A));\nNewExpression right = Expression.New(typeof(B));\nParameterExpression parameterExpression = Expression.Parameter(typeof(A), "p");\nUnaryExpression body = Expression.Convert(\n    parameterExpression, typeof(B),\n    (MethodInfo)MethodBase.GetMethodFromHandle((RuntimeMethodHandle)/*OpCode not supported: LdMemberToken*/));\nParameterExpression[] obj = new ParameterExpression[1];\nobj[0] = parameterExpression;\nExpression.Lambda<Func<B>>(\n    Expression.Coalesce(left, right, Expression.Lambda(body, obj)), Array.Empty<ParameterExpression>());\n
Run Code Online (Sandbox Code Playgroud)\n

替换GetMethodFromHandle为反射代码来获取A.op_Implicit,并且您有代码来创建Coalesce具有非 null 的有效表达式树Conversion

\n