【问题标题】:Passing anonymous/lambda functions to void delegates with no parameters? [C#]将匿名/lambda函数传递给没有参数的无效委托? [C#]
【发布时间】:2024-01-13 02:48:01
【问题描述】:

我一直在探索 C# 委托和匿名/lambda 函数,我对它们如何交互有一些疑问。据我了解,一个简单的委托对象可以具有预定义的返回和参数类型,并且任何具有相同返回/参数类型的命名函数都可以分配给委托。

然而,就我而言,我一直在开发一款游戏,玩家可以将多个命令排入他们的命令列表。在被添加到命令列表后的某个时刻,命令最终由玩家执行..

我开始通过为每个命令(AttackCommand、HealCommand 等)创建特定的子类来实现这一点,但后来发现在制作各种新命令的原型时这乏味。我认为,出于测试目的,创建一个带有成员委托的 CustomCommand 类会很酷,这样我就可以匿名定义和传递函数并快速迭代新的游戏设计理念。

public class CustomCommand : Command {

    public delegate void CommandDelegate();
    private CommandDelegate func;

    public CustomCommand( CommandDelegate del ){
        func = del;
    }

    public Execute(){
        func();
    }
}

然后 Player 有一个方法,它定义了一个匿名函数并使用它来构造一个 CustomCommand。然而,让我有点困惑的是,这个方法的参数似乎能够存储在 CustomCommand 的委托中:

//inside Player class..
//command to steal gold from a target!
public CreateCustomCommand( Player targetPlayer ){
    CustomCommand.CommandDelegate anonCommand = delegate()
    {
         //Steals up to amount passed..
         int goldToSteal = targetPlayer.StealGold(25);
         gold += goldToSteal;
    };

    CustomCommand newCommand = new CustomCommand( anonCommand );
    commandList.Enqueue( newCommand );
}

//Command is automatically executed later..

我试过这段代码,它不仅能编译,而且还能工作;钱从目标被盗并添加到源玩家。我很高兴它可以工作,但我不确定幕后到底发生了什么,这有点令人不安。

以下是我难以理解的内容:

在(Player)的 CreateCustomCommand 方法中,gold 在范围内,因为它是 Player 类的成员targetPlayer 在范围内,因为它是一个参数。 . 但是,这些东西是如何被封装在一个匿名函数中并传递给另一个对象内部的无效、无参数委托并且仍然有效的呢?这里发生了什么事?匿名函数的创建是否存储对其包含的所有对象的引用?

与将命名函数分配给具有匹配返回/参数类型的委托相比,这似乎非常灵活,但是有什么主要的缺点或陷阱吗?此外,这种技术是否与任何特定的设计模式或编程方法相关(或部分)?

【问题讨论】:

  • 如果这行不通,Lambda 将不会很有用。您需要使用的心理模型是编译器将它们重写为存储 this 的隐藏类。委托的目标是该类的对象。所以访问 this.gold 不是问题。谷歌“.net lambda capture”以了解更多信息。

标签: c# lambda delegates anonymous-function


【解决方案1】:

如果我理解正确,这是因为匿名函数使用了closures。闭包是可以capture (See: Variable Scope In Lambda Expressions) 生成它们的可变环境的代码块。

因此,由于 CreateCustomCommand 方法定义了一个匿名函数(闭包),匿名函数捕获并保持对 CreateCustomCommand 范围内的变量的访问。

感谢@HansPassant 为我指明了正确的方向!

【讨论】: