Manuel
Manuel

Reputation: 167

Passing Generic Type with Constraint as Type<Constraint>

If I have a GenericClass with a type constraint of where T : BaseType, why can't I pass it as GenericClass<BaseType>?

class GenericClass<T> where T : BaseType
{
    void Method()
    {
        new UtilityClass().Method(this);
    }
}

class UtilityClass
{
    internal void Method(GenericClass<BaseType> genericClass)
    {
    }
}

The static analyser error:

Cannot convert from 'GenericsPoc.GenericClass<T>' to 
'GenericsPoc.GenericClass<GenericsPoc.BaseType>'

I didn't expect this since UtilityClass<T> the T has to inherit from BaseClass.

Upvotes: 0

Views: 196

Answers (2)

Eric Lippert
Eric Lippert

Reputation: 660463

This question is asked every day. One more time!

class Cage<T> where T : Animal
{
  public void Add(T t) { ... }
}

Now your question is: why is this illegal?

Cage<Animal> cage = new Cage<Gerbil>();

Because this line is legal:

cage.Add(new Tiger());

Cage<Animal> has a method Add which takes Animal. A Tiger is an Animal so that must be legal. But that means that we just put a tiger into a gerbil cage.

Since that line must be legal and it causes a type error, some other line must be illegal, and now you know which one it is.

The feature you want is called generic covariance and it is legal in C# when:

  • The generic type is an interface or delegate
  • The varying type argument is a reference type
  • The interface or delegate has been marked as safe for variance.

For example:

IEnumerable<Animal> animals = new List<Tiger>() { new Tiger() };

That's legal. List<Tiger> implements IEnumerable<Tiger>, which is an interface and marked as safe for covariance, so it can be assigned to IEnumerable<Animal>.

You'll note that there is no way to put a duck into a sequence of animals, and therefore there is no way to put a duck into a sequence of tigers. That's how we know that it is safe for covariance.

Upvotes: 4

JuanR
JuanR

Reputation: 7803

You need to modify UtilityClass so that the method also passes down the generic type:

internal void Method<T>(GenericClass<T> genericClass) where T: BaseType
{
}

Upvotes: 2

Related Questions