【问题标题】:Covariance in Generics: Creating a Generic List with a Bounded Wildcard泛型中的协方差:使用有界通配符创建泛型列表
【发布时间】:2018-04-04 06:24:44
【问题描述】:

我整天都在寻找合适的解决方案,我对 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# 的任何解决方案。还是我走错了路?

【问题讨论】:

  • 不清楚你是否真的读过 c# 中的协方差?您似乎有合适的条件来查找信息,但实际上并没有在您的问题中提到它...您是否只需要链接到 docs.microsoft.com/en-us/dotnet/standard/generics/… 之类的文章,或者您是否已经尝试过但无法让它工作?
  • 我也找到了你提到的那篇文章,但是没有帮到我。据我了解,我的方法应该是可行的。也许我再也见不到树木了。
  • C# 没有使用点差异,只有声明点差异。

标签: c# generics inheritance covariance


【解决方案1】:

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 将只传递狗,而狗是哺乳动物。请注意,这是一个逆变转换。

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

【讨论】:

  • 感谢您解释清楚的评论。我对使用站点差异和声明站点差异做了一些进一步的阅读。我想要的(我认为)仍然是协方差转换。有一个 IAnimalStuff 的列表并向其中添加各种动物,例如IAnimalStuff。据我了解,如果没有通用修饰符,这应该是可能的。不幸的是,这只有在我的泛型接口只有返回 T 类型对象但不使用 T 类型对象的方法时才有可能,事实并非如此。所以我假设,我有某种设计问题。不过还是非常感谢你。
【解决方案2】:

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

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

这是将 AnimalStuff 的“动物”转换为类型“动物”。 相反,尝试使用接口作为您的基础IAnimal 并将您的集合定义为IAnimalStuff&lt;IAnimals&gt;。然后让Dog 继承自IAnimal,它应该可以完成你想要的。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-08-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-09-29
    • 2020-11-20
    相关资源
    最近更新 更多