对于基于图块的游戏,我使用不同的类来描述不同图块类型的行为。它们(显然)来自基类。现在我遇到了一个问题,有时我需要弄清楚玩家是否有足够的资金支付升级到某种磁贴类型的费用。
由于tile类型的成本始终保持不变,因此将其设置为静态似乎是有意义的。不幸的是,C#似乎不允许使用抽象类或接口来强制在子类中存在这样的静态字段。
我的“解决方案”是使用反射获取这些数据,但在我看来相当丑陋且有潜在危险,因为我可能会忘记其中一个子类中的静态字段,这会导致整个事情......
以下代码片段是我目前拥有的代码段; AllowedUpdates
是List<System.Type>
,其中包含可以升级到平铺的类型。
foreach (Type t in AllowedUpdates) {
// Get the Action Point Cost
FieldInfo fi = t.GetField ("actionPointCost", BindingFlags.NonPublic | BindingFlags.Static);
int cost = (int)fi.GetValue (null);
// Check for any requirements
bool requirementsFulfilled;
try {
// Get the static method that checks the necessary requirements.
MethodInfo mi = t.GetMethod ("CheckRequirements", new Type[] { typeof(Dictionary<string, ProtoObject>) });
object[] arguments = { neighbourFields };
// Invoke this method
object returnValue = mi.Invoke (null, arguments);
requirementsFulfilled = (bool)returnValue;
} catch (ArgumentNullException) {
// This type has no special requirements, pass it.
requirementsFulfilled = true;
} catch (NullReferenceException) {
// No requirements needed, pass it.
requirementsFulfilled = true;
}
}
必须有更好的方法来做到这一点。我忽略了一种设计模式吗?
答案 0 :(得分:6)
您无法通过抽象基类或接口强制在任何派生类上存在static
成员。
反思不是你最好的选择。在这种情况下,重新考虑您对static
类的使用。您可以将磁贴类型的成本设为abstract
只读属性。派生类将被迫实施该属性。
public abstract int ActionPointCost { get; }
答案 1 :(得分:0)
不,这是不可能的。您可能需要实例化一个实例并使用实例字段/属性(这可能比您当前正在进行的反射更快)。或者您可以保留Dictionary<Tile, int>
来存储您需要的值。
答案 2 :(得分:0)
将有关切片的元数据重构为另一个类层次结构,其中包含诸如成本等等。
答案 3 :(得分:0)
没有。 Interfaces
和Abstract classes
不允许您强制执行该操作。
原因是它们附加到这些类的实例而不是它们的定义。 Eric Lippert
有一篇很好的帖子,但我现在找不到这个链接。
我认为您可以强制执行的一种方法是在启动应用程序时使用Reflection
。如果每个必须具有特定静态成员的类都没有它,则应用程序失败。
其他方式可能是使用构建后操作来检查这些约束,例如Code Contracts
。
答案 4 :(得分:0)
您得出结论,静态字段是此问题的正确设计选择,但实现包含非常混乱的反射,迫使您接受您的设计选择。
我认为,如果实施变得凌乱,除了“似乎应该”之外没有任何收获,那么设计是错误的。
使其成为必须由构造函数设置的只读实例字段。说真的,你会让自己头疼。