Что за инструкция в коде ? Какая это тема?

358
04 февраля 2017, 07:13

c => c.FirstName
c => c.Email
c => c.Photo
Answer 1

Конструкции вида x => x.Email (а также более сложные, вида (x, y) => x.Email + y.Age или там n => { int s = 0; while (n-- > 0) s += n * n; return s; }) — это лямбда-выражения. Они используются в C# в двух смыслах.

Если в этом месте ожидается делегат/метод/что-то такое, то лямбда имеет смысл локально-определённой функции. Например, такой код:

Func<Entity, string> f = (c => c.FirstName);

по существу не отличается от кода

Func<Entity, string> f = GetFirstName;
static string GetFirstName(Entity c)
{
    return c.FirstName;
}

(но записывается быстрее). Кроме того (и это очень важно и удобно), лямбды могут ссылаться на локальные переменные и поля, видимые в точке определения. Например, так:

int x = hey.GimmeSomeX();
Func<int, int> f = n => n * x;

Такого эффекта уже нельзя добиться с локальной функцией, поэтому компилятор «под капотом» использует более сложную конструкцию.

Если же в месте, где упоминается лямбда, ожидается специальная штука под названием дерево выражений, то лямбда конвертируется в это самое дерево выражений. Имея дерево выражений, можно посмотреть программным путём, что же там внутри. В частности, если у вас есть c.FirstName, вы можете увидеть, что это обращение к полю с именем FirstName. Именно для этого оно используется в вашем примере.

Чем же дерево выражений лучше, чем просто передать строку "FirstName"? Дело в том, что в строке вы можете ошибиться, а вот за правильностью лямбда-выражения следит компилятор. Также при переименовании IDE сможет правильно подхватить изменения. А со строкой возможны, понятно, проблемы.

Answer 2

Подобный код используется в C# для того, чтобы указать имя свойства без риска опечататься. В классическом коде это могло бы выглядеть так:

modelBuilder.Entity<Customer>()
            .Property("FirstName")
            .IsRequired()
            .HasMaxLength(30);

Понятно, что при переименовании свойства FirstName код продолжает компилироваться, но перестаёт работать во время выполнения. Было бы неплохо использовать такую конструкцию, которая при переименовании свойства приводила бы к ошибке компиляции. Это давало бы нам возможность сразу обнаруживать и исправлять опечатки в названии полей.

Как раз для этого и применяют синтаксис, основанный на деревьях выражений:

public PrimitivePropertyConfiguration Property<T>(
Expression<Func<TStructuralType, T>> propertyExpression
)
where T : struct, new()
{
    . . .
}

Синтаксис страшноват, но он позволяет статически типизировать обращение к любому свойству класса. Что в данном случае происходит?

Func<TStructuralType, T> означает, что на вход ожидается лямбда-выражение, оно же анонимная (безымянная) функция. Синтаксис таких функций в C# выглядит как x => Exp(x), где x — это параметр функции, а Exp(x) — какое-то не очень сложное выражение. В нашем случае это простое выражение — обращение к свойству объекта.

Expression<Func<TStructuralType, T>> означает, что мы не будем выполнять функцию непосредственно, а вместо этого построим дерево выражения и сохраним его в переменной типа Expression.

Объект этого класса будет доступен во время выполнения и мы сможем пройтись по нему, и извлечь название свойства.

То, что это Expression именно от Func<TStructuralType, T> ограничивает способ задания параметра propertyExpression: мы ждём либо имя функции с одним параметром, либо анонимную функцию с одним параметром (она же лямбда).

Проще говоря, Expression<Func<TStructuralType, T>> означает, что при вызове Property в качестве параметра ожидается что-то вроде x => Exp(x). Вы всё ещё можете выстрелить себе в ногу и написать неподходящее выражение, например, x => x + x, но так делать всё-таки не стоит.

Наконец, в чём главная магия, как устроен метод Property? В основе, конечно, лежит рефлексия и подробности можно посмотреть в ответе на соответствующий вопрос на StackOverflow (англ.).

Непосредственно в Entity Framework методы, извлекающие имя свойства из выражения, вынесены в класс ExpressionExtensions.

READ ALSO
OracleDataReader GetDouble ошибка cast is not valid

OracleDataReader GetDouble ошибка cast is not valid

Добрый день! Видел много вопросов по этому поводу но ответа так и не нашёл

400
Как установить путь к 7z.dll?

Как установить путь к 7z.dll?

Установлены библиотеки через nuget: 1) SevenZipSharp

583
Как задать значение по умолчанию для свойства типа Font в PropertyGrid?

Как задать значение по умолчанию для свойства типа Font в PropertyGrid?

Для того, чтобы отобразить свойства и поля моего класса в PropertyGrid, я для них задаю атрибуты PropertyGridНапример :

416