5
votes

How to make a list hold all the different implementations of generic interface?

e.g

public class Animal { // some Animal implementations }
public class Dog : Animal { // some Dog implementations }
public class Snake : Animal { // some Snake implementations }

public interface ICatcher<T> where T: Animal
{
    // different animals can be caught different ways.
    string Catch(T animal);
}

public class DogCatcher : ICatcher<Dog> 
{ 
    string Catch(Dog animal) { // implementation }
}

public class SnakeCatcher : ICatcher<Snake> 
{ 
    string Catch(Snake animal) { // implementation }
}

I want to hold all the catcher in a list something like,

public class AnimalCatcher
{
     // this will hold the catching method an animal catcher knows (just something similar)
     public IEnumerable<ICatcher<Animal>> AnimalCatcher = new List<ICatcher<Animal>>
     {
          new DogCatcher(),
          new SnakeCatcher()
     }
}

I know that it is something to deal with generic modifiers in c# (Covariance, Contravariance and Invariance) but unable to get it working.

Tried: adding 'out' in

public interface ICatcher<out T> where T: Animal
{
    // different animals can be caught different ways.
    string Catch(T animal);
}

but gives a compile time error :

"The type parameter 'T' must be contravariantly valid on 'ICatcher.Catch(T)'. 'T' is covariant."

What am i doing wrong?

4
Did you try casting both the catchers (new DogCatcher() and new SnakeCatcher()) to ICatcher<Animal> ?Eric Wu
similar question is asked and well explained by Eric Lippert stackoverflow.com/questions/46893858/…Farhad Jabiyev
How are you going to use this list ?Fabjan

4 Answers

2
votes

You need some type unification for animals, and you need to remove the generic declaration of ICatcher and make it a concrete type:

public interface IAnimal {}

public class Dog : IAnimal {}
public class Snake : IAnimal {}

public interface ICatcher
{
    // different animals can be caught different ways.
    string Catch(IAnimal animal);
}

Then you can have a collection of catchers like so:

public class AnimalCatcher
{
     // this will hold the catching method an animal catcher knows (just something similar)
     public IEnumerable<ICatcher> AnimalCatcher = new List<ICatcher>
     {
          new DogCatcher(),
          new SnakeCatcher()
     }
}

ETA: Here's a repl.it demonstrating how it would be set up with interfaces. Although, in the respective Catch implementations, you are going to have to cast the IAnimal interface (if you need to access instance variables specific to a particular animal implementation)

2
votes

If Dog is Animal, it doesn't mean that ICatcher<Dog> is ICatcher<Animal>. There is no any relationship between ICatcher<Dog> and ICatcher<Animal>.

Microsoft made that decision to prevent run-time errors in case of contravariant T. Let's take a look:

ICatcher<Dog> dogCatcher = new DogCatcher();
ICatcher<Animal> animalCatcher = dogCatcher; 

// Then, we can pass `Snake` as input to the `animalCatcher` which is actually `DogCatcher`
animalCatcher.Catch(new Snake()); // ???

For saying that ICatcher<Dog> is ICatcher<Animal>, we have to use out keyword in front of T which will say that ICatcher<T> is covariant in T. And it means T will be used only as return type inside your interface . But, currently you are passing T as input to the Catch method.

0
votes

As your question does not say what the List is used for, I am making a wild guess at the problem you are trying to solve.

I expect what you need is

public interface ICatcher
{
     void MethodThatMakesYouListUseful()
}

public interface IPerAnimalCatcher<T> : ICatcher where T: Animal
{
    // different animals can be caught different ways.
    string Catch(T animal);
}

AnimalCatchers = new List<ICatcher>();

As there is no useful type relationship between generic interfaces unless you define it yourself.

0
votes

Consider introducing an ICatcher interface. Then inherit it in your ICatcher<T>.

At the end create an IEnumerable<ICatcher> instead of IEnumerable<ICatcher<Animal>>.

Generics in Microsoft libraries have the same approach.