I have a problem with generics in C# I hope you can help me out with.
public interface IElement { }
public interface IProvider<T> where T : IElement {
IEnumerable<T> Provide();
}
So far it's pretty simple. I want the providers to return enumerables of specific elements. A specific implementation of the interfaces is as follows:
public class MyElement : IElement { }
public class MyProvider : IProvider<MyElement> {
public IEnumerable<MyElement> Provide() {
[...]
}
}
But the problem comes now when I want to use it. This does not compile because it cannot implicitly convert MyProvider
into IProvider<IElement>
:
IProvider<IElement> provider = new MyProvider();
I have to do a cast to IProvider<IElement>
despite MyProvider
is an IProvider<MyElement>
and MyElement
is an IElement
. I could avoid the cast by making MyProvider
also implement IProvider<MyElement>
, but why does it not resolve the hierarchy in the type parameter?
EDIT: As per Thomas's suggestion, we can make it covariant in T
. But what if there are other methods like below where there are arguments of type T
?
public interface IProvider<T> where T : IElement {
IEnumerable<T> Provide();
void Add(T t);
}
You can also specify a generic parameter as a constraint. The type argument supplied for the type you're constraining must be or derive from the type of the constraint. This parameter is called a naked type constraint.
Using type parameters in generic constraints TypeScript allows you to declare a type parameter constrained by another type parameter. The following prop () function accepts an object and a property name. It returns the value of the property.
I’ll introduce you to the various ways you can constrain generic types in C#. You can constrain a generic type to a value type by setting the constraint of the type as follows. Here the struct keyword is used to constrain T to a value type.
You can't inherit from a generic type parameter because the type isn't known at compile time so the compiler can't figure out what the superclass is. I realise that, at first glance, the fact that the compiler can figure out what <T> is would seem to suggest that it should be able to figure out what T is, but the two things are different.
I have to do a cast to
IProvider<IElement>
despiteMyProvider
is anIProvider<MyElement>
andMyElement
is anIElement
. Why does it not resolve the hierarchy in the type parameter?
This is a very frequently asked question. Consider the following equivalent problem:
interface IAnimal {}
class Tiger : IAnimal {}
class Giraffe : IAnimal {}
class MyList : IList<Giraffe> { ... }
...
IList<IAnimal> m = new MyList();
Now your question is: "I have to do a cast to IList<IAnimal>
despite the fact that MyList
is an IList<Giraffe>
and Giraffe
is an IAnimal
. Why does this not work?"
It does not work because... suppose it did work:
m.Add(new Tiger());
m is a list of animals. You can add a tiger to a list of animals. But m is really a MyList, and a MyList can only contain giraffes! If we allowed this then you could add a tiger into a list of giraffes.
This must fail because IList<T>
has an Add method that takes a T. Now, maybe your interface has no methods that takes a T. In that case, you can mark the interface as covariant, and the compiler will verify that the interface is truly safe for variance and allow the variance you want.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With