【问题标题】:No closure for delegates in anonymous function inside foreach loopforeach 循环内匿名函数中的委托没有关闭
【发布时间】:2015-01-15 01:31:10
【问题描述】:

更新: 原来这是 Unity3D 使用的 Mono 编译器的问题。我不确定当前版本的 Mono (3.10.0) 是否修复了它,但引擎中使用的旧版本 (2.0.5) 似乎没有为代表实现此功能,或者只是按其应有的方式工作。


在我正在开发的游戏中(在 Unity3D 引擎中)的某个时刻,玩家会同时解锁多个项目。对于每一个,我必须提供一个简单的信息对话框,用户必须单击该对话框才能前进到下一个,直到全部阅读完毕。

我有一个简单的 C# 静态方法来显示一个对话框(在 Unity 中只是一个带有一些文本的彩色叠加层,与 C# UI 框架没有任何关系):

ConfirmationDialog.Create("Item X Unlocked!", callback);

当用户最终按下对话框时,callback 被调用。

我想链接所有对话框,以便每个对话框仅在单击前一个时创建,所以我尝试了这个:

Action callback = delegate {};

foreach (string item in unlockedItems) {
    var cb = callback;  // I though this would create a closure for delegates too
    callback = (() => ConfirmationDialog.Create(item + " Unlocked!", cb));
}
callback();

这在我的脑海中是有道理的,因为匿名函数将在循环的每次迭代中使用不同的“cb”委托。不过,我似乎弄错了,因为这段代码似乎会导致在单击时重复调用相同的对话框,就像在递归函数中发生的那样(我猜这就是它变成的样子)。

我知道我可以,例如,在 for 循环中执行 int value = i;(其中 i 是循环迭代器)以在匿名函数中使用正确的 i 值(我在这)。代表似乎不同,但特别是什么使他们的工作方式不同?还是我做错了什么?我可以以类似的方式进行这种链接吗?我说“相似”是因为我当然可以考虑以其他更复杂的方式来做这件事......

注意:如果我对“关闭”、“代表”等词的使用不正确,请纠正我:)

【问题讨论】:

  • 我知道这不是您要问的,但您似乎让这变得不必要地复杂了。既然你有一个循环,为什么不在循环中立即调用 ConfirmationDialog.Create() 。更少的代码,更少的复杂性。
  • 因为这样会一次创建所有对话框,我需要每个对话框按顺序显示。它们具有一定的透明度,因此玩家会看到最前面的那些,这是不可取的。还有其他问题:)
  • 这是从非 UI 线程创建对话框的问题吗?如果在 UI 线程上创建为模式对话框,您将一次看到一个(至少对于 WinForms 和 WPF...不确定您使用的是什么框架)。
  • 哦,对不起,可能不太清楚,但我没有使用任何 C# 的 UI 框架。我正在使用 Unity3D 开发游戏。碰巧我使用 C# 作为语言。我会澄清这个问题。
  • 无论是什么问题,它都是 Unity 和/或您的 ConfirmationDialog.Create() 实现所特有的。 IE。似乎callback 参数并没有像您认为的那样做。当我实现此代码的 WinForms 版本时,它完全符合我(和您)的期望:每个对话框都连续显示。确保您传递的 callback 参数实际上在用户关闭对话框之前不会被调用。您应该发布一个更完整的代码示例,其中包含 ConfirmationDialog.Create() 方法。见stackoverflow.com/help/mcve

标签: c# delegates closures anonymous-function


【解决方案1】:

您是否可以控制ConfirmationDialog.Create("Item X Unlocked!", callback); API?如果是这样,您应该考虑重构它以使用Task,而不是进行回调。如果你有这个,你可以简单地将一堆任务链接为延续,并让它们一个接一个地执行。

我知道在游戏开发中,这些事情在本质上通常是相当异步的,UI 的显示方式和随后的反应......所以你可以使用TaskCompletionSource 这样你就可以处理一些你可以发出信号的东西任务完成。

【讨论】:

  • 感谢您的回答! ConfirmationDialog 其实是我的一个类。不幸的是,Unity 的 Mono 编译器似乎是导致我的问题的原因(请参阅有问题的 cmets)。此外,它无法访问 .NET 拥有的异步库,因此我无法使用此解决方案 :( 我会找到替代方案。只是可能不像我希望的那么简单。再次感谢。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-31
  • 1970-01-01
  • 2010-11-01
  • 2016-03-14
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多