linq 表达式<TDelegate>赋值如何在语言语法级别工作



TLDR:这是如何编译的?

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

能够编译E e = x => 24;的最低class E是多少


一些调查

class E {};
E e = x => 24;

Visual Studio错误是"无法将lambda表达式转换为类型'E',因为它不是委托类型",这令人困惑,因为据我所知,委托是这样声明的:

int delegate MyHandle();

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

此外,我查看了Expression -> LambdaExpression -> Expression<TDelegate>的元数据,无法确定语法/声明使Expression<TDelegate>像委托一样行事。

更重要的是,我试图创建自己的class E来模仿Expression<TDelegate>,但我甚至无法编译该类

// 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;
}

收到错误"'Lambda表达式'不包含采用 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());

我试图了解信息"方法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>给予特殊处理;你无法对自己的类型获得相同的处理。

表达式树允许将 lambda 表达式表示为数据结构而不是可执行代码。表达式树是形式System.Linq.Expressions.Expression<D>的表达式树类型的值,其中D是任何委托类型。

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

这是一个 Lambda 表达式:

x => 24

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

Func<T, int>是一个委托函数,它将 T 作为输入并返回一个 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 应该是一个整数。函数的返回是 -x,这也是一个 int。

Expression是各种表达式的基类。表达式表示返回值的操作。最基本的表达式是常量,它们只表示一个常量值,如24.其他表达式可能是加法,它们将两个表达式作为输入,并具有一个输出值。其他表达式是返回"数字"的乘法、否定等,或返回布尔值的布尔表达式,如 AND OR NOR 等。

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

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

x => x.Length上面的语句创建了一个类型为System.Linq.Expressions.Expression<TDelegate>的对象,该对象类型为 LambdaExpression,因此您可以将其分配给Expression<Func<string, int>>

您的问题:

能够编译E e = x => 24的最低E类是多少?

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

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

关于您的模拟代码

显然,您有一个具有两个函数 Foo 和 Bar 的类 A,并且您想实例化一个 Mock 对象来模拟 A 的功能。

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

这说明,myAmock是一个对象,应该嘲笑类A的行为。每当有人调用Foo函数时,它都应该返回值 24。

最新更新