开发者

Is there a way to enforce the existence of a static member in a child class in C#?

开发者 https://www.devze.com 2023-01-31 23:57 出处:网络
For a tile-based game I\'m using different classes to describe the behaviour of the different tile types. They (obviously) derive from a base class. Now I have the problem that sometimes I need to fig

For a tile-based game I'm using different classes to describe the behaviour of the different tile types. They (obviously) derive from a base class. Now I have the problem that sometimes I need to figure out whether the player has enough funds to pay for an upgrade to a certain tile type.

Since the cost of a tile type remains the same all the time, it would seem to make sense to make that static. Unfortunately, C# does not seem to allow the use of abstract classes or interfaces to enforce the existence of such a static field in a child class.

My "solution" was to get this data using reflection, but it seems to me rather ugly and potentially dangerous, since I might forget the static field in one of the child classes, which would bring down the whole thing …

The following code snippet is what I currently have; AllowedUpdates is a List<System.Type> containing the types a tile can be upgraded to.

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开发者_JAVA技巧;
    } catch (NullReferenceException) {
        // No requirements needed, pass it.
        requirementsFulfilled = true;
    }
}

There must be a better way to do that. Is there a design pattern I overlooked?


You cannot enforce, either by an abstract base class or an interface, the existence of a static member on any derived class.

Reflection is not your best bet here. Reconsider your usage of static classes in this case. You might make the cost of a tile type an abstract read-only property. Derived classes will be forced to implement the property.

public abstract int ActionPointCost { get; }


No, this is not possible. You would either need to instantiate an instance and use an instance field/property (which could possibly be faster than the reflection you are currently doing). Or you can keep a Dictionary<Tile, int> that stores the value you need.


Refactor metadata about tiles to another class hierarchy where you have things like cost etc.


No. Interfaces and Abstract classes don't allow you to enforce that.

The reason is they are attached to instances of these classes than definitions of them. There is a good post by Eric Lippert on this but I can't find that link right now.

One way I think you could enforce is using Reflection on startup of your application. If every class that must have a specific static member does not have it, fail the application.

Other way could be using post-build action to check these constraints like Code Contracts do.


You came to the conclusion that a static field is the right design choice for this problem, but the implementation consists of very messy reflection to force the acceptance of your design choice.

I would argue that if the implementation becomes messy for no gain other than "it seems it should", then the design is wrong.

Make it a read-only instance field that must be set by the constructor. Seriously, you'll save yourself a headache.

0

精彩评论

暂无评论...
验证码 换一张
取 消