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 => 24
或x => 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 等。
一种特殊的表达式(因此是派生类(是LambdaExpression
。LambdaExpression
通常表示类似函数的对象:它具有零个或多个输入参数和一个输出参数。您可以使用 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
,则可以MyClass
object
当然,如果构造函数采用任何基类,如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。