代码块后自动调用方法

时间:2012-01-16 11:08:32

标签: c# xna design-patterns idisposable xbox360

我添加了在游戏中设定的时间间隔后可重复的动作概念 我有一个班级来管理是否可以执行某个动作 调用者通过调用CanDoAction来查询他们是否可以执行操作,如果是,请执行操作并记录他们已使用MarkActionDone执行操作。

if (WorldManager.CanDoAction(playerControlComponent.CreateBulletActionId))
{
    // Do the action

    WorldManager.MarkActionDone(playerControlComponent.CreateBulletActionId);
}

显然这可能容易出错,因为您可能忘记致电MarkActionDone,或者可能忘记致电CanDoAction进行检查。

理想情况下,我想保留一个类似的界面,不必传递Action或类似的东西,因为我在Xbox上运行,并且宁愿避免传递操作并调用它们。特别是由于动作通常依赖于周围的代码,因此必须涉及大量的闭包。

我在想某种方式(ab)使用IDisposeable接口,因为这样可以确保最后可以调用MarkActionDone,但我不认为我可以跳过使用块CanDoAction将是错误的。

有什么想法吗?

2 个答案:

答案 0 :(得分:4)

我首选的方法是将此逻辑作为WorldManager的实现细节(因为它定义了关于是否可以执行操作的规则),使用委托模式:

public class WorldManager
{
  public bool TryDoAction(ActionId actionId, Action action)
  {
    if (!this.CanDoAction(actionId)) return false;
    try
    {
      action();
      return true;
    }
    finally
    {
      this.MarkActionDone(actionId);
    }
  }

  private bool CanDoAction(ActionId actionId) { ... }
  private void MarkActionDone(ActionId actionId) { ... }
}

这似乎最适合SOLID主体,因为它避免了任何其他类必须“了解”WorldManager的'CanDoAction','MarkActionDone'实现细节。

<强>更新

使用AOP框架(例如PostSharp)可能是一个很好的选择,以确保以干净的方式将此方面添加到所有必需的代码块。

答案 1 :(得分:2)

如果您想最小化GC压力,我建议使用接口而不是代表。如果您使用IDisposable,则无法避免调用Dispose,但您可以让IDisposable实现使用标志来指示Dispose方法不应执行任何操作。除了代理人有一些内置语言支持这一事实之外,他们无法做任何接口所不能做到的事情,但接口提供了两个优于代表的优势:

  1. 使用绑定到某些数据的委托通常需要为数据创建堆对象,为委托本身创建第二个对象。接口不需要第二个堆实例。
  2. 在可以使用受限于接口的泛型类型而不是直接使用接口类型的情况下,可以避免创建任何堆实例,如下所述(因为后退格式化不会在列表项中工作)。将委托与静态方法以及该方法要使用的数据组合在一起的结构可以表现得很像委托,而不需要堆分配。

第二种方法的一个警告:虽然避免GC压力是一件好事,但第二种方法最终可能会在运行时创建大量类型。在大多数情况下,创建的类型数量是有界的,但在某些情况下它可以无限制地增加。我不确定是否有任何方便的方法来确定程序在静态分析足够的情况下可以生成的完整类型集合(在一般情况下,静态分析不够,确定是否有任何特定的生成的运行时类型将等同于停机问题,但正如许多程序在实践中可以静态地确定总是停止或永不停止,我希望在实践中可以经常识别一组封闭的类型程序可以在运行时生成。

修改

上面第2点的格式搞砸了。这是解释,清理。

定义类型ConditionalCleaner<T> : IDisposable,其中包含TAction<T>的实例(两者都在构造函数中提供 - 可能以Action<T>作为第一个参数) 。在IDisposable.Dispose()方法中,如果Action<T>为非null,则在T上调用它。在SkipDispose()方法中,将Action<T>归零。为方便起见,您可能还希望同样定义ConditionalCleaner<T,U>: IDisposable(也许是三个和四个参数版本),并且您可能希望定义一个带有泛型Create<T>的静态类ConditionalCleaner,{{1}等等方法(因此可以说例如Create<T,U>using (var cc = ConditionalCleaner.Create(Console.WriteLine, "ABCDEF") {...}以便在使用块退出时执行指示的操作。如果使用Lambda表达式,最大的要求是确保lambda表达式不会关闭来自调用函数的任何局部变量或参数;调用函数想要传递给lambda表达式的任何内容都必须是其显式参数。否则系统将定义一个类对象来保存任何已关闭的变量,如以及指向它的新代表。