Why can't I cast an item to a generic type — where the generic type is an interface, and after checking that the item implements said interface?

132 views Asked by At

Description

I'm trying to write effects (à la buffs/debuffs) for my game. Here's what I want to build:

The class EffectsManager houses all active effects. A class like HealthController queries it for health-related effects, i.e. effects that implement the IHealthEffect interface, and gets back a List containing only such effects.

Attempted Solution

Inside EffectsManager:

public List<T> GetEffects<T>() {
    List<T> effects = new List<T>();

    foreach (ActiveEffect activeEffect in activeEffects) {
        // ActiveEffect is just a wrapper for Effect that houses duration, time elapsed, etc.
        // I only need to return the Effect itself, which houses the logic.
        Effect effect = activeEffect.GetEffect();
        if (effect is T) {
            effects.Add((T)effect);
        }
    }

    return effects;
}

And then, the caller script would write:

List<IHealthEffect> healthEffects = effectsManager.GetEffects<IHealthEffect>();

Problem

Cannot convert type 'Effect' to 'T'

On effects.Add((T)effect);.

Similar Questions

Cannot implicitly convert type 'Int' to 'T'

But in my case, I'm doing not an implicit but an explicit cast. Why won't the compiler let me proceed, and should I use Convert.ChangeType() — as suggested there — anyway?

More Info

Effect is just an abstract class with only 1 member: public int priority;.

Since an effect may affect multiple attributes, such as health and mana, I decided to go with interfaces that demand that the implementer write a specific method, e.g.: void Execute(HealthController healthController);.

2

There are 2 answers

2
Marc Gravell On BEST ANSWER

Here you go:

   if (effect is T typed) {
        effects.Add(typed);
   }

As for why: you'd need an object cast in the middle: (T)(object)effect

2
Johnathan Barclay On

But in my case, I'm doing not an implicit but an explicit cast.

Effect and T are unrelated, so the compiler will attempt a conversion rather than a cast, and clearly no conversion has been defined between the types either, hence the error.

As suggested by Marc, you could make full use of pattern matching:

if (effect is T t) {
     effects.Add(t);
}

But if your code is exactly as posted, you can achieve this entirely through LINQ:

var healthEffects = activeEffects.Select(x => x.GetEffect()).OfType<IHealthEffect>();