C # без лямбда-выражения

1

У меня есть выражение лямбда:

cake.PropertyChanged += (mSender, eventArgs)
               => listOfFinishedOrders.Items.Insert(count,cake.NameOfCake + eventArgs.PropertyName);

Как написать это без использования лямбда-выражения?

  • 0
    Просто оберните код в метод и установите PropertyChanged для этого метода
  • 6
    Ваша лямбда, кажется, имеет по крайней мере одну, возможно две или более захваченных переменных. Существует несколько различных способов преобразования без лямбда-выражения, но если вы не предоставите хороший, минимальный , полный пример кода, показывающий точно полный контекст, будет невозможно дать лучший ответ. Пожалуйста, улучшите свой вопрос. Также было бы полезно понять, почему вы не хотите использовать лямбду; это полезная функция ... зачем пытаться ее избегать?
Теги:
lambda

3 ответа

3
Лучший ответ

При использовании выражения лямбда (очень распространенного) все, что действительно происходит, это код, объявляющий анонимные методы. Компилятор С# справляется с этим, создавая реальный метод, хотя и не имеющий видимого имени в исходном коде (отсюда "анонимный"). Чтобы избавиться от синтаксиса лямбды (опять же, почему?), "Все", которое вам нужно сделать, это переместить тело анонимного метода на именованный метод.

В простейшем случае новый именованный метод может быть помещен в один класс. Это работает, когда нет захваченных переменных; весь код в методе использует только те вещи, которые видны в любом обычном методе класса. Если это так, то вы можете сделать что-то вроде этого:

void M()
{
    // some other stuff

    cake.PropertyChanged += N;

    // some other stuff
}

void N(object mSender, EventArgs eventArgs)
{
    listOfFinishedOrders.Items.Insert(count, cake.NameOfCake + eventArgs.PropertyName);
}

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

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

Вы можете сами имитировать поведение компилятора С#:

class Captured1
{
    private readonly int count;
    private readonly Cake cake;
    private readonly ListBox listOfFinishedOrders;

    public Captured1(int count, Cake cake, ListBox listOfFinishedOrders)
    {
        this.count = count;
        this.cake = cake;
        this.listOfFinishedOrders = listOfFinishedOrders;
    }

    public void N(object mSender, EventArgs eventArgs)
    {
        listOfFinishedOrders.Items.Insert(count,
            cake.NameOfCake + eventArgs.PropertyName);
    }
}

void M()
{
    // some other stuff

    Captured1 c = new Captured1(count, cake, listOfFinishedOrders);

    cake.PropertyChanged += c.N;

    // some other stuff
}

Примечание. Я взял некоторые свободы выше, угадывая такие типы, как Cake и ListBox. Вы не дали достаточного контекста в своем вопросе, чтобы кто-нибудь мог точно знать, что это за переменные, поэтому я просто старался изо всех сил. Я предполагаю, что вы можете предоставить правильные типы в своем собственном коде, если я ошибся.

Теперь, в приведенном выше, обратите внимание, что все поля доступны только для чтения. Они копируются в новый класс при создании экземпляра. Но это может быть или не быть тем, что вы действительно хотите. В частности, в реальном сценарии лямбда изменения в захваченных переменных наблюдаются кодом в модуле лямбда-метода, даже после создания делегата лямбда.

Без хорошего, минимального, полного примера кода я не хочу размышлять над этим конкретным примером. Он может вообще не применяться в вашем сценарии.

Но в целом вы бы решить, что такой сценарий, сделав поле public, а не readonly для readonly, так что она может быть использована в качестве фактической переменной в контексте, в котором создается экземпляр. Это позволит изменять видимость переменной в элементе лямбда-метода, а также разрешать изменения переменной, которая возникает в теле тела лямбда, видимо для выполнения кода после вызова делегата лямбда.

Отмечу, что ваше событие является участником самого объекта cake. Эти сильные намеки (хотя опять же, отсутствие хорошего примера кода не гарантируют), что объект mSender на самом деле всегда будет объектом cake. Если это так, вы можете упростить класс Captured1, не включая поле cake, и изменить метод N() чтобы выглядеть так:

public void N(object mSender, EventArgs eventArgs)
{
    listOfFinishedOrders.Items.Insert(count,
        ((Cake)mSender).NameOfCake + eventArgs.PropertyName);
}


Все вышеперечисленное является упрощенной версией того, что делает компилятор. Существует несколько вариантов сценария, и для перечисления всех возможностей здесь потребуется существенно переопределить поведение компилятора, слишком широкую задачу для StackOverflow. Но я надеюсь, что вышесказанное дает вам достаточно хорошее представление об основном подходе:

  • Переместите тело анонимного метода в именованный метод. Если это может скомпилироваться без ошибок, тогда... отлично, все готово!
  • Если это не сработало, то одна или несколько переменных в именованном методе более недействительны (выведя их из области, в которой они определены). В этом случае вам нужно будет создать вспомогательный класс (как первый Captured1), где вы можете сохранить значения переменных. Именованный метод также войдет в этот новый класс. Создайте экземпляр класса и используйте именованный метод для подписки на событие (или что-то еще... lambdas используются многими другими способами, и эта базовая техника будет работать в любом из этих сценариев).
0

Если вы не хотите использовать лямбда там, вам придется использовать отдельный метод. В этот момент вы столкнетесь с проблемой, что используете переменные, которые могут или не могут быть доступны вашему методу, поскольку они объявлены локально. Если это относится к listOfFinishedOrders и/или count, вам нужно будет хранить их где-нибудь, где метод может обращаться к ним тоже (обычно как частные члены в классе):

public class SomeClass
{
    // private members for those local variables
    private int count;
    private ListBox listOfFinishedOrders;

    public void SomeMethod ()
    {
        // Some code that has 'cake', 'listOfFinishedOrders' and 'count'
        // as local variables. This is also where the lambda was before.

        // store the values in the type
        this.count = count;
        this.listOfFinishedOrders = listOfFinishedOrders;

        cake.PropertyChanged += CakeChanged;
    }

    private void CakeChanged (object sender, EventArgs e)
    {
        Cake cake = sender as Cake; // sender is the original cake

        // 'listOfFinishedOrders' and 'count' are instance members
        listOfFinishedOrders.Items.Insert(count, cake.NameOfCake + e.PropertyName);
    }
}

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

В зависимости от вашей реальной ситуации могут быть более чистые способы ее решения, где-то посередине между решением Peters, которое очень близко имитирует функциональность лямбда, и мое, что просто предполагает простую среду.

  • 1
    Выше может работать в определенных сценариях. Тем не менее, вы должны указать, что это определенно ограниченный подход; это не будет работать, например, если есть несколько объектов cake , каждый из которых нуждается в своей собственной копии захваченных переменных, потому что они захвачены в разных контекстах (например, в цикле).
-1

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

class MyClass
{
    public MyClass() { cake.PropertyChanged += HandleCakeChanged; }
    void HandleCakeChanged(object sender, EventArgs args)
    {
        // do stuff
    }
}
  • 0
    И насколько хорошо это работает, если listOfFinishedOrder , count и / или cake были локальными переменными в исходном контексте? Вы пропустили все интересные части ответа.
  • 0
    IMO, это станет очевидным, как только OP получит синтаксис для извлечения этой строки из лямбды и попытается это сделать.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню