泛型中的协方差:使用有界通配符创建通用列表

时间:2017-10-23 16:09:08

标签: c# generics inheritance covariance

我正在寻找一整天的正确解决方案,我对C#相当新。 如果我是对的,想要类似于Java代码的东西

ArrayList<IAnimalStuff<? extends Animal>> ianimals = new ArrayList<>();

仅适用于C#。或者当我走错路时另一个解决方案。

详细情景: 我有一个基类(Animal)和多个子类(例如Dog)。

class Animal
{
}
class Dog : Animal
{
}

我创建了一个所有动物的共同列表,其中包含各种不同动物的对象。

List<Animal> animals = new List<Animal>();
animals.add(new Dog()); // and so on

此外,我有一个界面和一个类,用于从这个界面派生的每个特殊动物。

interface IAnimalStuff<TAnimal> where TAnimal : Animal
{
    void doSomething(TAnimal animal);
}

public class DogStuff : IAnimalStuff<Dog>
{
    public override void doSomething(Dog animal) 
    {
    }
}

现在我想用Animals管理一个列表,用AnimalStuff管理一个列表。当循环遍历所有动物时,我想在另一个列表中执行对狗有效的所有Animalstuff。虽然动物列表没有问题,但我有问题要创建另一个列表。

List<IAnimalStuff<Animals>> ianimals = new List<IAnimalStuff<Animals>>();

与第一个列表不同,我只能将对象添加到此类型列表

IAnimalStuff<Animals>

,但我也想做

ianimals.add(GetDogStuff()); // add object of type IAnimalStuff<Dog>

我认为这是有效的,因为Dog是Animal的子类。我认为Java代码的上线可以解决,但我找不到任何C#解决方案。还是我走错了路?

2 个答案:

答案 0 :(得分:4)

C#具有声明站点方差,而不像Java那样使用站点方差。

在C#中你可以这样做:

interface IAnimalStuff<in TAnimal> where TAnimal : Animal // note "in"
{
    void doSomething(TAnimal animal);
}

然后你可以说

IAnimalStuff<Mammal> iasm = new MammalStuff();
IAnimalStuff<Dog> iasd = iasm;

为什么这样做?因为iasm.doSomething需要任何哺乳动物,iasd.doSomething只能通过狗,而狗只是哺乳动物。请注意,这是逆变转换。

但你不能走另一条路;你不能说“狗是哺乳动物,因此狗是哺乳动物”。哺乳动物可以接受长颈鹿,但狗不能。这将是协变转换。

答案 1 :(得分:0)

我认为问题可能出在您的清单声明中:

List<IAnimalStuff<Animals>> ianimals = new List<IAnimalStuff<Animals>>();

这是将AnimalStuff的“动物”投射到“动物”类型。 相反,请尝试使用界面作为基础IAnimal并将您的集合定义为IAnimalStuff<IAnimals>。然后Dog继承IAnimal,它应该完成你想要的。