linq Expression<TDelegate>赋值如何在语言语法级别上工作

How does linq Expression<TDelegate> assignment work on a language syntax level

TLDR:如何编译?

class A{};
Expression<Func<A, int>> e = x => 24; // I don't understant what makes this compile
                                      // and what happens at this assignment

能编译的最小值是多少class EE e = x => 24;


一些调查

class E {};

E e = x => 24;

Visual Studio 错误是 "Cannot convert lambda expression to type 'E' because it is not a delegate type" 这令人困惑,因为据我所知,委托是这样声明的:

int delegate MyHandle();

而且我没有找到任何方法让 class 成为代表。

此外,我查看了 Expression -> LambdaExpression -> Expression<TDelegate> 的元数据,但无法确定是什么 syntax/declaration 使 Expression<TDelegate> 像代表一样行事。

甚至我还尝试创建自己的 class E 来模仿 Expression<TDelegate>,但我什至无法编译 class

// basically a copy-paste of the metadata of `Expression<TDelegate>`
public sealed class E<TDelegate> : LambdaExpression
{

    public TDelegate Compile() => default(TDelegate);
    public TDelegate Compile(DebugInfoGenerator debugInfoGenerator) => default(TDelegate);
    public TDelegate Compile(bool preferInterpretation) => default(TDelegate);
    public Expression Update(Expression body, IEnumerable<ParameterExpression> parameters)
        => default(Expression);
    protected override Expression Accept(ExpressionVisitor visitor) => null;
}

出现错误“'LambdaExpression' 不包含采用 0 个参数的构造函数”


上下文(可跳过):

我正在开始使用 Moq,因为我不能想当然,所以我想了解它是如何工作的/它是如何实施的。因此,这是有关该问题的一系列调查的第一部分。

我现在特地想了解一下

public class A
{
    public virtual int Foo() => throw new NotImplementedException();
    public virtual int Bar() => throw new NotImplementedException();
}

var a = new Mock<A>();

a.Setup(x => x.Foo()).Returns(24); // ??
Console.WriteLine(a.Object.Foo());

我正在尝试了解信息 "the method Foo" 是如何传递给 a 通过传递那个 lambda?据我所知,lambda 只是一个可调用对象,但不知何故 a 神奇地知道了 lambda 的实际主体,即如果你通过 x => 24x => x.Foo() + x.Bar() 将抛出但会接受 x => x.Foo()

而且我看到 Setup 参数的类型是 Expression<Func<A, int>> 因此我现在的问题。

C# 规范对 System.Linq.Expressions.Expression<D> 进行了特殊处理;你不能为你自己的类型得到同样的待遇。

Expression trees permit lambda expressions to be represented as data structures instead of executable code. Expression trees are values of expression tree types of the form System.Linq.Expressions.Expression<D>, where D is any delegate type.

来源:https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/language-specification/types#expression-tree-types

这是一个 Lambda 表达式:

x => 24

根据MSDN Lambda expressions,lambda 表达式可用于创建委托表达式树类型

A Func<T, int> 是一个委托函数,它接受一个 T 作为输入,returns 一个 int 作为输出。如果将上面的lambda表达式赋值给这个Func,那么编译器会认为lambda表达式中的x是Func的输入,=>之后的部分是Func的输出:

Func<string, int> func = x => x.Length;

这里的 x 应该是一个字符串,=> 之后的部分可以使用,但不是必须使用,这个 x 来产生必须是 int 的输出。

Func<int, int> func = x => x = -x;

这里的 x 应该是一个整数。函数的 return 是 -x 也是一个 int.

Expression是各种表达式的基础class。表达式表示 return 一个值的操作。最基本的表达式是常量,它们只表示一个常量值,如 24。其他表达式可能是加法,将两个表达式作为输入并具有一个输出值。其他表达式是乘法、求反等,return 一个 "number",或布尔表达式,如 AND OR NOR 等,return 一个布尔值。

一种特殊的表达式(因此派生 class)是 LambdaExpressionLambdaExpression 通常代表一个类似函数的对象:它有零个或多个输入参数和一个输出参数。您使用 lambda 表达式为 LambdaExpression 赋值(注意 lambda 表达式中的 space!):

Expression<Func<string, int>> myExpression = x => x.Length;

我之前写过 lambda 表达式 x => x.Length 的作用。上面的语句创建了一个 System.Linq.Expressions.Expression<TDelegate>, which is of type LambdaExpression 类型的对象,因此您可以将它分配给 Expression<Func<string, int>>.

您的问题:

What is the minimum class E to be able to compile E e = x => 24?

您的 class E 将需要一个接受 System.Linq.Expressions.Expression<Func<MyClass, int> 作为参数的构造函数(或赋值运算符)。如果你不使用 lambda 表达式中的 x MyClass 可以是 object

当然,如果构造函数采用任何基数 class,如 Expression,也可以接受。但是,如果这样做,您将无法使用任何 Expression<Func<MyClass, int> 功能。

关于您的 Mock 代码

显然你有一个 class A 有两个函数 Foo 和 Bar,你想实例化一个 Mock 对象来模拟 A 的功能。

var myAmock = new Mock<A>();
myAmock.Setup(x => x.Foo()).Returns(24);

这表示,myAmock 是一个应该模拟 class A 行为的对象。每当有人调用 Foo 函数时,它应该 return 值为 24。