4

TLDR: How does this compile?

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

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


Some investigation

class E {};

E e = x => 24;

Visual Studio error is "Cannot convert lambda expression to type 'E' because it is not a delegate type" which is confusing because as far as I know a delegate is declared like this:

int delegate MyHandle();

and I didn't find any way of making a class a delegate.

Furthermore I have looked at the metadata of Expression -> LambdaExpression -> Expression<TDelegate> and wasn't able to identify what syntax/declaration makes Expression<TDelegate> act like a delegate.

Even more I have tried to create my own class E to mimic Expression<TDelegate> and I wasn't even able to compile the 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;
}

getting the error "'LambdaExpression' does not contain a constructor that takes 0 arguments"


Context (can be skipped):

I am getting started with Moq and because I just cannot take things for granted I am trying to understand how it works / how it is implemented. So this the first part of a series of inquiries about that.

I am now specifically trying to understand

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());

I am trying to understand how the information "the method Foo" is passed to a by passing that lambda? As far as I know a lambda is just a callable object, but somehow magically a knows the actual body of the lambda, i.e. will throw if you pass x => 24 or x => x.Foo() + x.Bar() but will accept x => x.Foo()

And I see that Setup parameter is of type Expression<Func<A, int>> hence my current question.

bolov
  • 72,283
  • 15
  • 145
  • 224
  • To be precise, the error _Cannot convert lambda expression to type 'E' because it is not a delegate type_ could be expanded to _Cannot convert lambda expression to type 'E' because it is neither a delegate type nor a generic type constructed from 'System.Linq.Expressions.Expression'_. – Jeppe Stig Nielsen Sep 25 '18 at 07:35

2 Answers2

5

The C# specification gives special treatment to System.Linq.Expressions.Expression<D>; you cannot get the same treatment for your own type.

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.

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

Ben Voigt
  • 277,958
  • 43
  • 419
  • 720
0

This is a Lambda expression:

x => 24

According to MSDN Lambda expressions a lambda expression can be used to create a delegate or an expression tree type.

A Func<T, int> is a delegate function that takes a T as input and returns an int as output. If you assign the lambda expression above to this Func, then the compiler assumes that the x in the lambda expression is the input of the Func, and the part after the => is the output of the Func:

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

Here x is expected to be a string, the part after the => may use, but is not obliged to use, this x to produce the output which has to be an int.

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

Here x is expected to be an int. The return of the function is -x which is also an int.

Expression is the base class of all kinds of expressions. Expressions represent an operation that return a value. The most basic expressions are constants, they represent just a constant value, like 24. Other expressions might be additions, which take as input two expressions and have one output value. Other expressions are multiplication, negation, etc, which return a "number", or boolean expressions like AND OR NOR etc which return a Boolean.

A special kind of expression (so a derived class) is a LambdaExpression. A LambdaExpression usually represents a function-like object: it has zero or more input parameters and one output parameter. You assign a value to a LambdaExpression using a lambda expression (note the space in lambda expression!):

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

I wrote earlier what the lambda expression x => x.Length does. the statement above creates an object of type System.Linq.Expressions.Expression<TDelegate>, which is of type LambdaExpression, hence you can assign it to an Expression<Func<string, int>>.

Your question:

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

Your class E will need a constructor (or assignment operator) that accepts a System.Linq.Expressions.Expression<Func<MyClass, int> as parameter. If you don't use the x from your lambda expression MyClass can be object

Of course it is also accepted if the constructor takes any of the base classes, like Expression. However, if you do so you won't be able to used any of the Expression<Func<MyClass, int> functionality.

About your Mock code

Apparently you have a class A with two functions Foo and Bar, and you want to instantiate a Mock object to mock the functionality of A.

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

This says, that myAmock is an object that should mock behaviour of class A. Whenever someone calls the Foo function it should return a value of 24.

Harald Coppoolse
  • 28,834
  • 7
  • 67
  • 116
  • Thank you for your answer. I know what a lambda is. I know what an expression tree is. I know what the mock does. What I didn't know was that lambdas are treated as data (an expression tree) when assigned to an `Expression` which Ben Voigt explained. – bolov Sep 24 '18 at 18:13