doppelgreener
doppelgreener

Reputation: 5094

Can I define a Type variable for which Types representing subtypes of a specific class are the only valid values?

Background

I'm writing some components (this kind of component) for a small personal game project. In this system, entities have various types of components which belong to various categories. For example, the IController category of components includes KeyboardController and AiController. An entity has a collection of components, and should only have one component from each category. All components inherit from IComponent.

Components have a MetaType property which should report a type they correspond to, in order to say: "Hey, please treat me as this type of component!" This property returns a Type object. The AiController returns typeof(IController), telling the Entity to treat this as its controller. Other valid MetaTypes for it would be typeof(AiController) or typeof(IComponent). It should not be able to return any arbitrary type, e.g. typeof(int) - just component types.

My problem

Currently my components can report any arbitrary type for a MetaType. The AIController can actually return typeof(int), for instance - that's a valid Type object, after all.

Can I constrain the Type values such that the only valid types would be the type of any class or interface for which IComponent is an ancestor? I imagine such a variable declaration might look like this:

Type<IComponent> component; // This can only store certain types
Type where Type : IComponent component; // This too

I am specifically interested in whether this is possible - not so much in alternate approaches (I am aware of several, and they include just allowing this behaviour, since I'm the only one working with this code.

Upvotes: 3

Views: 1137

Answers (2)

Chris Sinclair
Chris Sinclair

Reputation: 23208

You could create a MetaType object whose constructors or factory methods would take a generic type constrained against IComponent and provide access to the non-constrained Type. But since its constructors are constrained, you should be guaranteed to not get other non-IComponents.

public class MetaType
{
    public Type ComponentType { get; private set; }

    private MetaType(Type componentType)
    {
        this.ComponentType = componentType;
    }

    public static MetaType Create<T>() where T : IComponent
    {
        return new MetaType(typeof(T));
    }
}

Your usage might look like:

MetaType validType = MetaType.Create<IComponent>(); //fine
MetaType validType = MetaType.Create<IController>(); //fine
MetaType validType = MetaType.Create<AIController>(); //fine

MetaType invalidType = MetaType.Create<int>(); //compiler error!

EDIT: I'm assuming that your IController interface inherits from IComponent, but if it doesn't, you can add factory overloads like CreateController and CreateComponent each one constrained against the unique interface.

Upvotes: 4

Animatinator
Animatinator

Reputation: 410

Not directly, unfortunately - whilst you can constrain type parameters in generics, type variables like your MetaType field can't be constrained. It's basically like trying to constrain an int: you can use exceptions to make sure it's never set to an invalid value but ultimately the variable itself could be anything of the right type.

Upvotes: 1

Related Questions