Я пытался узнать больше об использовании деревьев выражений Lamba и таким образом, я создал простой пример. Вот код, это работает в LINQPad, если вставляется в как программа C#.
void Main()
{
IEnumerable<User> list = GetUsers().Where(NameContains("a"));
list.Dump("Users");
}
// Methods
public IEnumerable<User> GetUsers()
{
yield return new User{Name = "andrew"};
yield return new User{Name = "rob"};
yield return new User{Name = "chris"};
yield return new User{Name = "ryan"};
}
public Expression<Func<User, bool>> NameContains(string namePart)
{
return u => u.Name.Contains(namePart);
}
// Classes
public class User
{
public string Name { get; set; }
}
Это приводит к следующей ошибке:
Аргументы типа для метода 'Система. Linq. Счетный. Где (Система. Наборы. Универсальный. IEnumerable, Система. Func)', не может быть выведен из использования. Попытайтесь определить аргументы типа явно.
Однако, если я просто заменяю первой строкой в основном с этим:
IEnumerable<User> list = GetUsers().Where(u => u.Name.Contains("a"));
Это хорошо работает. Может сказать мне, что я делаю неправильно?
Метод Enumerable.Where
принимает Func
, а не Expression
. Возможно, вы путаете с Queryable.Where
, который действительно принимает выражение в качестве параметра... В вашем случае вам не нужно выражение, вам просто нужен делегат, который может быть выполнен против каждого элемента в последовательности. Цель выражений (в основном) - быть проанализированными и переведенными во что-то другое (например, SQL), чтобы выполнить запрос к внешнему источнику данных
Измените тип возвращаемого значения NameContains
с Expression
на просто ] Func <Пользователь, Bool>
.В этой ситуации нет необходимости возвращать Expression, вы действительно хотите вернуть скомпилированный делегат. Есть разница между выражением, составляющим лямбду, и самой лямбдой (которая является делегатом).
Если вы отправляете лямбду в метод, метод может принимать лямбда либо как выражение, либо как скомпилированный тип делегата, в зависимости от того, что вы указываете в параметрах. Если тип входящего параметра является выражением, вы можете отправить что-то похожее на делегата, однако, если метод ожидает делегата, вы должны передать ему скомпилированный делегат, а не просто выражение. При этом вы также можете сделать что-то вроде:
var certainUsers = GetUsers().Where(NameContains("a").Compile());
Которая скомпилирует выражение и вернет Func
.
Лямбда-выражения можно рассматривать как код (делегаты) или как данные (деревья выражений)
В вашем примере вы пытаетесь обработать лямбда-выражение как код.
Вы можете использовать объявление Expression <>, если хотите обрабатывать лямбда-выражение как данные.
Зачем вам это нужно?
Вот цитата из книги Linq In Action,
«Деревья выражений могут быть переданы инструментам во время выполнения, которые используют их, чтобы направлять их выполнение или преобразовывать их во что-то другое, например SQL в случае LINQ to SQL."
Использование деревьев выражений позволяет вам взять лямбда-выражение и преобразовать его в данные, именно так работает Linq в SQL, он принимает лямбда-выражение, операторы запроса или выражения запроса и преобразует их в SQL. Вы, конечно, можете просмотреть и изменить созданное дерево выражений после преобразования в sql.