Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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.

like image 944
bolov Avatar asked Sep 18 '25 14:09

bolov


1 Answers

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

like image 119
Ben Voigt Avatar answered Sep 20 '25 03:09

Ben Voigt