【问题标题】:What is a correct way of replacing the switch block and Enum in this situation (C#)?在这种情况下(C#)替换开关块和枚举的正确方法是什么?
【发布时间】:2011-09-09 00:00:34
【问题描述】:

如果有帮助,以下问题是在我正在构建的游戏的背景下进行的。

在几个不同的地方,我有以下场景。存在一个父类,对于这个名为 Skill 的示例,我有许多子类实现父类中的方法。还有另一个父类,我们称之为 Vocation。这些技能需要在不同的职业子类中列出。但是,这些技能需要适用于游戏中使用任何给定职业的任何事物。

我当前的设置是有一个名为 Skill.Id 的 Enum,以便 Vocation 包含来自该 Enum 的值的集合,并且当游戏中的实体采用该 Vocation 时,该集合被传递到另一个名为 SkillFactory 的类中。每次我创建一个新的 Skill 子类时,Skill.Id 都需要一个新条目,以及新子类的构造函数的 switch 块中的一个案例。

即:

//Skill.Id
Enum{FireSkill,WaterSkill,etc}

//SkillFactory
public static Skill Create(Skill.Id id)
{
    switch(id)
    {
        case Skill.Id.FireSkill:
             return new FireSkill();
        //etc
    }
}

这工作得非常好,但是使用枚举和 switch 块作为一个过渡感觉比我解决这个问题所需的开销更大。有没有更优雅的方法来创建这些技能子类的实例,但仍然允许职业包含一个标识它可以使用的技能的集合?

编辑:只要 Vocation 可以包含一个允许任意实例化 Skill 子类的集合,我就可以扔掉枚举和相关的 switch 块。

【问题讨论】:

  • Switch 和逻辑 if 语句非常快,除了几乎每个工厂实现(我见过)都使用 switch 做同样的事情。恕我直言,我不会改变它。
  • 我同意这一点,前提是您没有在类型内的多个例程之间重复切换。此时,字典查找通常是更好的方法,因为您可以将“开关”放在代码中的单个位置。
  • 这是最快最易读的方式,为什么还要另寻他法?
  • 我不担心 switch 块和枚举的任何开销性能。我担心信息的重复(新的子类实现不仅需要实现,还需要新的 Skill.Id 条目和随后的 SkillFactory 案例语句)表明我的方法可能会更好。

标签: c# enums xna switch-statement


【解决方案1】:

您可以创建一个Dictionary<Skill.Id, Func<Skill>> 并使用它来实例化。

在构造函数中:

Dictionary<Skill.Id, Func<Skill>> creationMethods = new Dictionary<Skill.Id, Func<Skill>>();
public SkillFactory()
{
     creationMethods.Add(Skill.Id.FireSkill, () => new FireSkill());
     creationMethods.Add(Skill.Id.WaterSkill, () => new WaterSkill());
}

那么,你的 Create 方法就变成了:

public static Skill Create(Skill.Id id)
{
     return creationMethods[id]();
}

当然,这并没有好多少 - 除了它允许您将其扩展到每个 ID 的其他功能,而无需复制开关块(如果需要)。 (只需将更多内容放入字典的价值方面。)

话虽如此,从长远来看,完全摆脱枚举对于可扩展性来说是一个很好的好处。然而,这将需要更精细的改变。例如,如果您使用 MEF,您可以在运行时导入一组 SkillFactory 类型,并通过单个 ImportMany 将它们关联到一个名称(通过元数据)。这将允许您在不更改工厂的情况下添加新的技能子类,并通过名称或其他机制引用它们。

【讨论】:

    【解决方案2】:

    如果这个创建函数被如此使用以至于“case”会产生开销,那么带有枚举键的字典会产生很多垃圾。

    在 xna 游戏的上下文中,它可能比“案例”更糟糕。

    “如果您使用枚举类型作为字典键,内部字典操作将导致装箱。您可以通过使用整数键来避免这种情况,并在将枚举值添加到字典之前将它们转换为整数。” Extracted from here

    您可以使用简单的数组并将枚举转换为 int 进行索引:

     Enum {FireSkill=0,WaterSkill=1,etc}
    
     Func<Skill>[] CreationMethods = new Func<Skill>() 
     {
         () => new FireSkill(),
         () => new WaterSkill(),
     } 
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-03-26
      • 1970-01-01
      • 1970-01-01
      • 2010-12-25
      • 2012-12-02
      • 1970-01-01
      • 2018-05-03
      • 1970-01-01
      相关资源
      最近更新 更多