Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Constrained generics with hierarchy in type parameter

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);
}
like image 819
Julián Urbano Avatar asked Feb 04 '12 02:02

Julián Urbano


People also ask

How to specify a generic parameter as a constraint?

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.

How to use type parameters in generic constraints in typescript?

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.

How to constrain a generic type to a value type?

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.

Why can't you inherit from a generic type parameter?

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.


1 Answers

I have to do a cast to IProvider<IElement> despite MyProvider is an IProvider<MyElement> and MyElement is an IElement. 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.

like image 122
Eric Lippert Avatar answered Oct 16 '22 22:10

Eric Lippert