У меня есть выражение лямбда:
cake.PropertyChanged += (mSender, eventArgs)
=> listOfFinishedOrders.Items.Insert(count,cake.NameOfCake + eventArgs.PropertyName);
Как написать это без использования лямбда-выражения?
При использовании выражения лямбда (очень распространенного) все, что действительно происходит, это код, объявляющий анонимные методы. Компилятор С# справляется с этим, создавая реальный метод, хотя и не имеющий видимого имени в исходном коде (отсюда "анонимный"). Чтобы избавиться от синтаксиса лямбды (опять же, почему?), "Все", которое вам нужно сделать, это переместить тело анонимного метода на именованный метод.
В простейшем случае новый именованный метод может быть помещен в один класс. Это работает, когда нет захваченных переменных; весь код в методе использует только те вещи, которые видны в любом обычном методе класса. Если это так, то вы можете сделать что-то вроде этого:
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 используются многими другими способами, и эта базовая техника будет работать в любом из этих сценариев). Если вы не хотите использовать лямбда там, вам придется использовать отдельный метод. В этот момент вы столкнетесь с проблемой, что используете переменные, которые могут или не могут быть доступны вашему методу, поскольку они объявлены локально. Если это относится к 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, которое очень близко имитирует функциональность лямбда, и мое, что просто предполагает простую среду.
cake
, каждый из которых нуждается в своей собственной копии захваченных переменных, потому что они захвачены в разных контекстах (например, в цикле).
Я не уверен, что это хорошая идея написать этот код без лямбды, так как это один лайнер (идеальный вариант использования для лямбда). Но вот как вы могли это сделать, если тело лямбда длиннее:
class MyClass
{
public MyClass() { cake.PropertyChanged += HandleCakeChanged; }
void HandleCakeChanged(object sender, EventArgs args)
{
// do stuff
}
}
listOfFinishedOrder
, count
и / или cake
были локальными переменными в исходном контексте? Вы пропустили все интересные части ответа.