linq Expression <tdelegate>分配在语言语法级别如何工作

时间:2018-09-21 14:40:22

标签: c# linq delegates moq linq-expressions

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 E的最小E e = x => 24;是多少


一些调查

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

出现错误“'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());

我正在尝试了解信息“方法Foo”如何传递给a 通过那个lambda?据我所知,lambda只是一个可调用的对象,但是a以某种方式神奇地知道了lambda的实际主体,即如果您通过x => 24x => x.Foo() + x.Bar()但会接受{ {1}}

我看到x => x.Foo()参数的类型为Setup,因此是我当前的问题。

2 个答案:

答案 0 :(得分:4)

C#规范对System.Linq.Expressions.Expression<D>进行了特殊处理;您无法针对自己的类型获得相同的待遇。

  

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

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

答案 1 :(得分:0)

这是Lambda表达式:

x => 24

根据MSDN Lambda expressions,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,它也是一个整数。

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

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

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

我之前写过lambda表达式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。