泛型中的协变:使用有界通配符创建泛型列表
Covariance in Generics: Creating a Generic List with a Bounded Wildcard
我整天都在寻找合适的解决方案,但我对 C# 还很陌生。
如果我是对的,想要类似于 Java 代码
的东西
ArrayList<IAnimalStuff<? extends Animal>> ianimals = new ArrayList<>();
仅适用于 C#。或者当我走错路时的另一种解决方案。
详细场景:
我有一个基础class(动物)和多个子classes(例如狗)。
class Animal
{
}
class Dog : Animal
{
}
我创建了一个所有动物的通用列表,其中包含各种不同动物的对象。
List<Animal> animals = new List<Animal>();
animals.add(new Dog()); // and so on
此外,我有一个接口和一个class用于从该接口派生的每个特殊动物。
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 的子class。我认为用 Java 代码的上一行可以解决这个问题,但我没有找到 C# 的任何解决方案。还是我走错路了?
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
只会传递狗,而狗是哺乳动物。请注意,这是一个 逆变 转换。
但你不能走另一条路;你不能说 "dog is a mammal, therefore a dogstuff is a mammalstuff"。哺乳动物可以接受长颈鹿,但狗不能。那将是一个协变转换。
我认为问题可能出在您的列表声明中:
List<IAnimalStuff<Animals>> ianimals = new List<IAnimalStuff<Animals>>();
这是将 AnimalStuff 的 "animal" 转换为类型 "Animal"。
相反,请尝试使用接口作为基础 IAnimal
并将集合定义为 IAnimalStuff<IAnimals>
。然后让 Dog
从 IAnimal
继承,它应该可以完成你想要的。
我整天都在寻找合适的解决方案,但我对 C# 还很陌生。 如果我是对的,想要类似于 Java 代码
的东西ArrayList<IAnimalStuff<? extends Animal>> ianimals = new ArrayList<>();
仅适用于 C#。或者当我走错路时的另一种解决方案。
详细场景: 我有一个基础class(动物)和多个子classes(例如狗)。
class Animal
{
}
class Dog : Animal
{
}
我创建了一个所有动物的通用列表,其中包含各种不同动物的对象。
List<Animal> animals = new List<Animal>();
animals.add(new Dog()); // and so on
此外,我有一个接口和一个class用于从该接口派生的每个特殊动物。
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 的子class。我认为用 Java 代码的上一行可以解决这个问题,但我没有找到 C# 的任何解决方案。还是我走错路了?
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
只会传递狗,而狗是哺乳动物。请注意,这是一个 逆变 转换。
但你不能走另一条路;你不能说 "dog is a mammal, therefore a dogstuff is a mammalstuff"。哺乳动物可以接受长颈鹿,但狗不能。那将是一个协变转换。
我认为问题可能出在您的列表声明中:
List<IAnimalStuff<Animals>> ianimals = new List<IAnimalStuff<Animals>>();
这是将 AnimalStuff 的 "animal" 转换为类型 "Animal"。
相反,请尝试使用接口作为基础 IAnimal
并将集合定义为 IAnimalStuff<IAnimals>
。然后让 Dog
从 IAnimal
继承,它应该可以完成你想要的。