Как работает назначение linq Expression <TDelegate>на уровне синтаксиса языка - PullRequest
0 голосов
/ 21 сентября 2018

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: «Невозможно преобразовать лямбда-выражение в тип« 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 и потому что я просто не могу взять что-токак само собой разумеющееся, я пытаюсь понять, как это работает / как это реализуетсяmented.Так что это первая часть серии запросов об этом.

Я сейчас специально пытаюсь понять

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, передав эту лямбду?Насколько я знаю, лямбда - это просто вызываемый объект, но каким-то волшебным образом a знает фактическое тело лямбды, то есть, если вы передадите x => 24 или x => x.Foo() + x.Bar(), но примет x => x.Foo()

И я вижу, что параметр Setup имеет тип Expression<Func<A, int>>, следовательно, мой текущий вопрос.

Ответы [ 2 ]

0 голосов
/ 24 сентября 2018

Это лямбда-выражение:

x => 24

В соответствии с Лямбда-выражениями MSDN лямбда-выражение можно использовать для создания делегата или тип дерева выражений .

A Func<T, int> - это функция-делегат, которая принимает T в качестве входных данных и возвращает int в качестве выходных.Если вы назначите лямбда-выражение выше для этого Func, то компилятор предполагает, что x в лямбда-выражении является входом Func, а часть после => является выходом Func:

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

Здесь x, как ожидается, будет строкой, часть после => может использовать, но не обязана использовать, этот x для получения вывода, который должен быть целым числом.

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

Здесь ожидается, что x будет int.Функция возвращает -x, который также является целым числом.

Expression - базовый класс всех видов выражений.Выражения представляют операцию, которая возвращает значение.Основными выражениями являются константы, они представляют собой просто постоянное значение, например 24.Другие выражения могут быть дополнениями, которые принимают в качестве входных данных два выражения и имеют одно выходное значение.Другими выражениями являются умножение, отрицание и т. Д., Которые возвращают «число», или логические выражения, такие как AND OR NOR и т. Д., Которые возвращают логическое значение.

Специальный вид выражения (таким образом, производный класс) - LambdaExpression.LambdaExpression обычно представляет объект, подобный функции: он имеет ноль или более входных параметров и один выходной параметр.Вы присваиваете значение LambdaExpression, используя лямбда-выражение (обратите внимание на пробел в лямбда-выражении!):

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

Я писал ранее, что делает лямбда-выражение x => x.Length.Приведенное выше утверждение создает объект типа System.Linq.Expressions.Expression<TDelegate>, который имеет тип LambdaExpression , поэтому вы можете назначить его на Expression<Func<string, int>>.

Ваш вопрос:

Какой минимальный класс E для компиляции E e = x => 24?

Вашему классу E понадобится конструктор (или оператор присваивания), который принимает System.Linq.Expressions.Expression<Func<MyClass, int> в качестве параметра.Если вы не используете x из своего лямбда-выражения, MyClass может быть object

Конечно, это также допустимо, если конструктор принимает какой-либо из базовых классов, например Expression.Однако, если вы сделаете это, вы не сможете использовать любую из функций Expression<Func<MyClass, int>.

О вашем коде Mock

Очевидно, у вас есть класс Aс двумя функциями Foo и Bar, и вы хотите создать экземпляр объекта Mock для имитации функциональности A.

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

Это говорит о том, что myAmock является объектом, который должен имитировать поведение класса A,Всякий раз, когда кто-то вызывает функцию Foo, он должен возвращать значение 24.

0 голосов
/ 21 сентября 2018

Спецификация C # дает особый подход к System.Linq.Expressions.Expression<D>;Вы не можете получить ту же самую обработку для своего собственного типа.

Деревья выражений позволяют лямбда-выражениям быть представленными как структуры данных вместо исполняемого кода.Деревья выражений - это значения типов деревьев выражений в форме System.Linq.Expressions.Expression<D>, где D - любой тип делегата.

Источник: https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/language-specification/types#expression-tree-types

...