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

3

我整天都在寻找一个合适的解决方案,但我对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) 
    {
    }
}

现在我希望管理一个动物列表和一个动物材料列表。当遍历所有动物时,我想执行在另一个列表中对于狗有效的所有动物材料。虽然动物列表没有问题,但我在创建另一个列表方面遇到了问题。
List<IAnimalStuff<Animals>> ianimals = new List<IAnimalStuff<Animals>>();

不同于第一个列表,我只能向这个类型为的列表添加对象。
IAnimalStuff<Animals>

,但我也想要做

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

我认为这是有效的,因为狗是动物的子类。我认为使用Java代码的上一行可以解决这个问题,但是我没有找到C#的解决方案。或者我走错了吗?


{btsdaf} - Chris
我也找到了你提到的那篇文章,但是对我没有帮助。据我理解,我的方法应该是可行的。也许我已经看不到问题的本质了。 - z0tti
2
{btsdaf} - Eric Lippert
2个回答

6

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只会传递狗,而狗是哺乳动物。请注意,这是一个反变换。但你不能反过来说“狗是哺乳动物,因此狗杂物是哺乳动物杂物”。哺乳动物杂物可以接受长颈鹿,但狗杂物不能。那将是一个协变换

{btsdaf} - z0tti

0

我认为问题可能出在您对列表的声明上:

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

这里是将AnimalStuff的"animal"强制转换为类型"Animal"。相反,尝试使用接口作为您的基础IAnimal并将您的集合定义为IAnimalStuff<IAnimals>。然后让Dog继承自IAnimal,它应该可以实现您想要的功能。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接