У меня есть следующий сценарий:
Метод, который получает список людей из определенного класса, которые короче некоторого порога высоты.Я использую метод расширения Где, чтобы отфильтровать их.Поскольку предикат зависит от некоторых внешних данных, переменные classId
и height
становятся частью лямбда-замыкания.
public List<Person> getPeopleFromClassShorterThanLimit(int classId, int height)
{
Database db = new Database();
return db.Persons.Where(p => p.Height <= height && p.ClassId == classId).ToList();
}
Проблема в том, что иногда предикат усложняется, поэтому я хочу извлечь его вдругой метод:
public List<Person> getPeopleFromClassLowerThanLimit(int classId, int height)
{
Database db = new Database();
return db.Persons.Where(isLowerThan).ToList();
}
private bool isLowerThan(Person person)
{
}
Однако закрытие здесь становится проблемой, потому что я не могу передать переменные в функцию предиката.Я могу сделать функцию делегата встроенной (не используя лямбда-выражения), чтобы предикат выглядел как функция, но тогда эта функция также должна быть в том же методе, что и вызов Where()
.В большинстве сценариев это нормально, , но иногда я хочу, чтобы внешняя функция (неизвестная во время компиляции) вызывалась как предикат Where
.
. Самое близкое к этому решениеэто так:
public delegate bool ExtendedPredicate<T, TArg>(T argument, TArg[] arguments);
public static class Extended
{
public static IEnumerable<T> Where<T, TArg>(this IEnumerable<T> collection, ExtendedPredicate<T, TArg> predicate, params TArg[] arguments)
{
foreach (T item in collection)
{
if (predicate(item, arguments))
yield return item;
}
yield break;
}
}
public List<Person> getPeopleFromClassShorterThanLimit(int classId, int height)
{
Database db = new Database();
return db.Persons.Where(isFromClassShorterThanLimit, classId, height).ToList();
}
public bool isFromClassShorterThanLimit(Person person, int[] arguments)
{
return person.Height <= arguments[0] && person.ClassId == arguments[1];
}
но я нахожу это несколько узким, а также, для каждого метода расширения должна быть записана новая перегрузка.В дополнение ко всему этому, я думаю, EntityFramework не сможет преобразовать такие выражения в разумный оператор SQL (не уверен в этом, я не эксперт в том, как EF генерирует SQL из лямбд, но это дикая догадка). (переменное число аргументов может или не может быть предоставлено)
Вопрос:
То, что я хотел бы знать, есть ли какие-либоумный способ добиться этого с обычными делегатами (не заставляя определенные во время компиляции функции использовать в качестве предиката) без таких хаков, как показано выше?