Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unable to hold generic interface implementations in list c#

Tags:

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?

like image 472
kartik rajan Avatar asked Nov 11 '19 14:11

kartik rajan


2 Answers

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)

like image 156
gabriel.hayes Avatar answered Oct 12 '22 21:10

gabriel.hayes


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.

like image 40
Farhad Jabiyev Avatar answered Oct 12 '22 22:10

Farhad Jabiyev