问题
I am looking the whole day for a proper solution, bit I am fairly new to C#. If I am right, want something similar to the Java code
ArrayList<IAnimalStuff<? extends Animal>> ianimals = new ArrayList<>();
just for C#. Or another solution when I am on the wrong way.
Detailed Scenario: I have a base class (Animal) and multiple subclasses (e.g. Dog).
class Animal
{
}
class Dog : Animal
{
}
I create a common list of all animals, which contains objects of all kinds of different animals.
List<Animal> animals = new List<Animal>();
animals.add(new Dog()); // and so on
Additionally, I have an interface and a class for each special animal derived from this interface.
interface IAnimalStuff<TAnimal> where TAnimal : Animal
{
void doSomething(TAnimal animal);
}
public class DogStuff : IAnimalStuff<Dog>
{
public override void doSomething(Dog animal)
{
}
}
Now I want to manage one list with Animals and one list with AnimalStuff. When looping over all animals, I want to perform all Animalstuff in the other list which are valid for a dog. While a list of animals is no problem, I have my problems to create the other list.
List<IAnimalStuff<Animals>> ianimals = new List<IAnimalStuff<Animals>>();
Unlike in the first list, I only can add objects to this list of type
IAnimalStuff<Animals>
, but I also want to do
ianimals.add(GetDogStuff()); // add object of type IAnimalStuff<Dog>
I assumed this is valid, because Dog is a subclass of Animal. I think with the upper line of Java code this can be solved, but I did not find any solution for C#. Or am I on the wrong track?
回答1:
C# has declaration-site variance, not use-site variance like Java.
In C# you could do this:
interface IAnimalStuff<in TAnimal> where TAnimal : Animal // note "in"
{
void doSomething(TAnimal animal);
}
And then you could say
IAnimalStuff<Mammal> iasm = new MammalStuff();
IAnimalStuff<Dog> iasd = iasm;
Why does this work? Because iasm.doSomething takes any mammal, and iasd.doSomething will be passed only dogs, and dogs are mammals. Notice that this is a contravariant conversion.
But you cannot go the other way; you can't say "dog is a mammal, therefore a dogstuff is a mammalstuff". That mammalstuff can accept a giraffe, but a dogstuff cannot. That would be a covariant conversion.
回答2:
I believe the issue might be in your declaration of your list:
List<IAnimalStuff<Animals>> ianimals = new List<IAnimalStuff<Animals>>();
This is casting the "animal" of AnimalStuff to type "Animal".
Instead, try using an Interface as your base IAnimal and defining your collection as IAnimalStuff<IAnimals>. Then have Dog inherit from IAnimal and it should accomplish what you want.
来源:https://stackoverflow.com/questions/46893858/covariance-in-generics-creating-a-generic-list-with-a-bounded-wildcard