Подтвердить что ты не робот

Объединение лямбда-функций в С#

Используя С# 3.5, я хотел создать предикат для отправки в предложение where по частям. Я создал очень простое консольное приложение, чтобы проиллюстрировать решение, к которому я пришел. Это прекрасно работает. Абсолютно отлично. Но у меня нет идеи, как и почему.

    public static Func<Tran, bool> GetPredicate()
    {
        Func<Tran, bool> predicate = null;
        predicate += t => t.Response == "00";
        predicate += t => t.Amount < 100;
        return predicate;
    }

Когда я говорю "предикат + =", что это значит? предикат - = ничего не делает, и ^ =, & =, * =,/= не нравятся компилятору.

Компилятор не любит 'predicate = predicate + t = > t.Response....'.

На что я наткнулся? Я знаю, что он делает, но как он это делает?

Если кто-то хочет углубиться в более сложные лямбды, сделайте это.

4b9b3361

Ответ 1

"delegate + = method" - это оператор для многоадресного делегирования для объединения метода для делегирования. С другой стороны, "delegate - = method" - это оператор для удаления метода из делегата. Это полезно для Action.

Action action = Method1;
action += Method2;
action += Method3;
action -= Method2;
action();

В этом случае будут выполняться только Method1 и Method3, Method2 не будет запущен, потому что вы удалите этот метод перед вызовом делегата.

Если вы используете делегат multicast с Func, результатом будет последний метод.

Func<int> func = () => 1;
func += () => 2;
func += () => 3;
int result = func();

В этом случае результат будет равен 3, так как метод "() = > 3" - последний метод, добавленный к делегату. В любом случае будет вызываться весь метод.

В вашем случае будет эффективен метод "t = > t.Amount < 100".

Если вы хотите совместить предикат, я предлагаю эти методы расширения.

public static Func<T, bool> AndAlso<T>(
    this Func<T, bool> predicate1, 
    Func<T, bool> predicate2) 
{
    return arg => predicate1(arg) && predicate2(arg);
}

public static Func<T, bool> OrElse<T>(
    this Func<T, bool> predicate1, 
    Func<T, bool> predicate2) 
{
    return arg => predicate1(arg) || predicate2(arg);
}

Использование

public static Func<Tran, bool> GetPredicate() {
    Func<Tran, bool> predicate = null;
    predicate = t => t.Response == "00";
    predicate = predicate.AndAlso(t => t.Amount < 100);
    return predicate;
}

EDIT: исправьте имя методов расширения, как подсказывает Кейт.

Ответ 2

Когда вы используете + = или - = при делегировании, это просто дает вызов Delegate.Combine и Delegate.Remove.

Важное значение для делегатов многоадресной рассылки состоит в том, что возвращаемое значение всех, кроме последнего выполненного делегата, игнорируется. Все они исполняются (если не выбрано исключение), но используется только последнее возвращаемое значение.

Для предикатов вы можете сделать что-то вроде:

public static Func<T, bool> And<T>(params Func<T, bool>[] predicates)
{
    return t => predicates.All(predicate => predicate(t));
}

public static Func<T, bool> Or<T>(params Func<T, bool>[] predicates)
{
    return t => predicates.Any(predicate => predicate(t));
}

Вы бы сделали:

Func<string, bool> predicate = And<string>(
    t => t.Length > 10,
    t => t.Length < 20);

EDIT: здесь более общее решение, которое довольно забавно, если немного странно...

public static Func<TInput, TOuput> Combine<TInput, TOutput>
    (Func<TOutput, TOutput, TOutput> aggregator,
     params Func<TInput, TOuput>[] delegates) {

    // delegates[0] provides the initial value
    return t => delegates.Skip(1).Aggregate(delegates[0](t), aggregator);
}

Итак, вы могли бы реализовать И как:

public static Func<T, bool> And<T>(params Func<T, bool>[] predicates) {
    return Combine<T, bool>((x, y) => x && y, predicates);
}

(Я лично предпочитаю это с помощью GetInvocationList(), потому что вы получаете предикат, который вы можете передать другим битам LINQ и т.д.)

Ответ 3

Собственно, это не работает. Попробуйте протестировать его в случае, когда первое условие НЕИСПРАВНО, а второе. Вы обнаружите, что оно вернет true. Причина в том, что при работе с делегатами многоадресной передачи, возвращающими значения, возвращается только последнее значение. Например:

        Func<string, bool> predicate = null;
        predicate += t => t.Length > 10;
        predicate += t => t.Length < 20;

        bool b = predicate("12345");

Это вернет TRUE, потому что последний вызов функции возвращает true (менее 20). Чтобы действительно заставить его работать, вам нужно позвонить:

predicate.GetInvocationList();

который возвращает массив делегатов. Затем вам нужно убедиться, что они ВСЕ вернутся к истине, чтобы конечный результат был правдой. Есть смысл?

Ответ 4

Расширение ответа BFree (+ 1'd)

Если вы хотите получить поведение, которое вы ищете, вам нужно явно связать предикаты вместе. Вот пример

public static Func<Tran, bool> GetPredicate()
{
    Func<Tran, bool> predicate1 = t => t.Response == "00";
    Func<Tran, bool> predicate2 = t => t.Amount < 100;
    return t => predicate1(t) && predicate2(t);
}

Ответ 5

Если вы хотите совместить предикаты, попробуйте это:

public static Predicate<T> Combine<T>(params Predicate<T>[] predicates)
{
return t => predicates.All(pr => pr(t));
}

Вы называете это следующим:

Predicate<string> shortAndSweet = Combine<string>
(
    s => s.Length < 10,  // short,
    s => s == "sweet"    // and sweet
);

Ответ 6

+ = синтаксический сахар, специально реализованный для поддержки добавления обработчиков к событиям. Поскольку события - это всего лишь особый случай делегирования, а Func также является делегатом, синтаксис, похоже, работает здесь.

Но вы уверены, что он работает так, как ожидалось? Под этим я имею в виду, вы ожидаете оценки AND или OR? Как бы вы реализовали противоположное, если хотите? Вы уверены, что это не просто возвращение результата первого? Или последнее?