C#没有lambda表达式

时间:2015-03-30 05:49:22

标签: c# lambda

我有lambda表达式:

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

如何在不使用lambda表达式的情况下编写它?

3 个答案:

答案 0 :(得分:3)

在lambda表达式(一个非常常见的表达式)的这种用法中,所有真正发生的是代码声明了一个匿名方法。 C#编译器通过创建一个真正的方法来处理这个问题,尽管源代码中没有可见名称(因此是“匿名”)。为了摆脱lambda语法(再次,为什么?),你需要做的“全部”是将匿名方法体移动到命名方法。

在最简单的情况下,新命名的方法可以放在同一个类中。当没有捕获变量时,这可以工作;该方法中的所有代码仅使用在类的任何常规方法中可见的内容。如果是这种情况,那么你可以这样做:

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);
}

但是,在许多情况下,匿名方法会“捕获”一个或多个变量。在这种情况下,C#编译器将生成一个包含(并包含)匿名方法的类。在这个类中,还会有字段来支持“捕获”的变量。

实际上,在您的示例中,匿名方法体中使用的一个或多个变量很可能是捕获变量。

您可以自己模拟C#编译器的行为:

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
}

注意:我已经采取了一些自由,猜测CakeListBox等类型。你没有在你的问题中提供足够的上下文让任何人知道这些变量是什么,所以我只是尽我所能。我假设如果我猜错了,你可以在自己的代码中提供正确的类型。

现在,在上面,请注意所有字段都是只读的。创建实例时,它们将复制到新类。但这可能是也可能不是你真正想要的。特别是,在真正的lambda场景中,即使在创建了lambda委托之后,lambda方法体中的代码也会观察到对捕获变量的更改。

没有a good, minimal, complete code example我不愿意推测这个特定的例子。在您的方案中可能根本不适用。

但总的来说:你可以通过创建字段public而不是readonly来解决这种情况,以便它可以在创建实例的上下文中用作实际变量。这将允许变量在lambda方法体内可见,并允许对lambda方法体内发生的变量的更改对调用lambda委托后执行的代码可见。

我会注意到您的活动是cake对象本身的成员。这强烈暗示(尽管缺乏一个好的代码示例并不能保证)mSender对象实际上总是成为cake对象。如果是这样,您可以通过不包含Captured1字段并将cake方法更改为如下所示来简化N()类:

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


以上所有内容都是编译器实际执行的简化版本。这个场景有很多变化,并且列举这里的所有可能性将基本上重新实现编译器行为,这个任务对于StackOverflow来说太宽泛了。但我希望上面的内容能够让您对基本方法有足够的了解:

  • 将匿名方法主体移动到命名方法中。如果可以编译没有错误,那么......太棒了,你已经完成了!
  • 如果这不起作用,则命名方法中的一个或多个变量不再有效(将它们从定义它们的范围中取出)。在这种情况下,您需要创建一个辅助类(如上面的Captured1),您可以在其中存储变量值。命名方法也将进入新类。创建一个类的实例并使用命名方法来订阅事件(或者其他任何...... lambda用于许多其他方式,这种基本技术将适用于任何这些场景。)

答案 1 :(得分:0)

如果您不想在那里使用lambda,则必须使用单独的方法。此时,您将遇到使用变量的问题,因为它们是在本地声明的,因此您的方法可能会访问这些变量,也可能无法访问这些变量。如果是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);
    }
}

当然,此解决方案假设countlistOfFinishedOrders只有一个值可能不是这种情况(特别是如果您有多个蛋糕,您也想要附加事件 - 但是开箱即用,因为蛋糕是由事件发送者确定的。

根据你的实际情况,可能会有更简洁的方法来解决它,在Peter的解决方案中,中间位置非常接近地模仿lambdas的功能,而我的解决方案只是假设一个简单的环境。

答案 2 :(得分:-1)

我不确定在没有lambda的情况下编写这个代码是个好主意,因为它是一个内衬(lambdas的理想用例)。但是如果lambda体长了,你可以这样做:

class MyClass
{
    public MyClass() { cake.PropertyChanged += HandleCakeChanged; }
    void HandleCakeChanged(object sender, EventArgs args)
    {
        // do stuff
    }
}