如果有帮助的话,以下问题是在我正在构建的游戏的背景下提出的。
在几个不同的地方我有以下场景。存在一个父类,在本示例中称为 Skill,并且我有许多子类实现父类中的方法。还有另一个父类,我们称之为 Vocation。这些技能需要列在职业的不同子类中。然而,这些技能需要适用于游戏中使用任何给定职业的任何事物。
我当前的设置是有一个名为 Skill.Id 的枚举,以便 Vocation 包含该 Enum 中的值的集合,并且当游戏中的实体采用该职业时,该集合将传递到另一个名为 SkillFactory 的类中。每次创建新的 Skill 子类时,Skill.Id 都需要一个新条目,以及新子类构造函数的 switch 块中的一个 case。
即:
//Skill.Id
Enum{FireSkill,WaterSkill,etc}
//SkillFactory
public static Skill Create(Skill.Id id)
{
switch(id)
{
case Skill.Id.FireSkill:
return new FireSkill();
//etc
}
}
这工作得很好,但是使用枚举和开关块作为中间的过渡感觉比我解决这个问题所需的开销更多。是否有更优雅的方法来创建这些技能子类的实例,但仍然允许职业包含标识它可以使用的技能的集合?
编辑:我可以扔掉枚举和关联的开关块,只要 Vocation 可以包含允许任意实例化 Skill 子类的集合。
If it helps, the following question is in the context of a game I am building.
In a few different places I have the following scenario. There exists a parent class, for this example called Skill, and I have a number of sub-classes implementing the methods from the parent class. There also exists another parent class that we will call Vocation. The skills need to be listed in different sub-classes of Vocation. However, those skills need to be available for anything in the game that uses any given vocation.
My current setup is to have an Enum called Skill.Id, so that Vocation contains a collection of values from that Enum and when an entity in the game takes on that Vocation the collection is passed into another class, called SkillFactory. Skill.Id needs a new entry every time I create a new Skill sub-class, as well as a case in the switch block for the new sub-classes' constructor.
i.e.:
//Skill.Id
Enum{FireSkill,WaterSkill,etc}
//SkillFactory
public static Skill Create(Skill.Id id)
{
switch(id)
{
case Skill.Id.FireSkill:
return new FireSkill();
//etc
}
}
This works perfectly fine, but using the enum and switch block as a go between feels like more overhead than I need to solve this problem. Is there a more elegant way to create instances of these Skill sub-classes, but still allows Vocation to contains a collection identifying the skills it can use?
Edit: I am fine throwing out the enum and associated switch block, so long as Vocation can contain a collection that allows arbitrary instantiation of the Skill sub-classes.
发布评论
评论(2)
您可以创建一个
Dictionary>
并使用它来实例化。在构造函数中:
然后,您的 Create 方法变为:
当然,这并没有好多少 - 除了它确实允许您将其扩展到每个 ID 的其他功能,而无需复制 switch 块(如果这成为要求)。 (只需将更多内容放入字典的值部分即可。)
话虽这么说,从长远来看,完全摆脱枚举对于可扩展性来说是一个很好的好处。然而,这将需要更复杂的改变。例如,如果您使用 MEF,则可以在运行时导入一组
SkillFactory
类型,并通过单个ImportMany
将它们与名称(通过元数据)关联。这将允许您在不更改工厂的情况下添加新的技能子类,并通过名称或其他机制引用它们。You can make a
Dictionary<Skill.Id, Func<Skill>>
and use it to instantiate.In the constructor:
Then, your Create method becomes:
Granted, this isn't much better - except that it does allow you to extend this to other functionality that's per ID without duplicating the switch block if that becomes a requirement. (Just put more into the value side of the Dictionary.)
That being said, in the long run, getting rid of the enum entirely can be a good benefit for extensibility. This will require a more elaborate change, however. For example, if you used MEF, you could import a set of
SkillFactory
types at runtime and associate them to a name (via metadata) via a singleImportMany
. This would allow you to add new Skill subclasses without changing your factory, and refer to them by name or some other mechanism.如果这个创建函数的使用方式会导致“case”产生开销,那么带有枚举键的字典将生成大量垃圾。
在 xna 游戏的背景下,它可能比“案例”更糟糕。
“如果您使用枚举类型作为字典键,内部字典操作将导致装箱。您可以通过使用整数键来避免这种情况,并在将枚举值添加到字典之前将它们转换为整数。” 从此处提取
您可以使用一个简单的数组并将 enum 转换为 int 进行索引:
if this creation function is going to be so used that a "case" will produce overhead, dictionary with enums keys will generate a lot of garbage.
In the context of a xna game, it can be worse than the "case".
"If you use an enum type as a dictionary key, internal dictionary operations will cause boxing. You can avoid this by using integer keys, and casting your enum values to ints before adding them to the dictionary." Extracted from here
You can use a simple array and cast enum to int for indexing: