C#为什么我不能将键入子类的hashset添加到键入其超类的hashset中

Yng*_*sen 9 c# generics collections hashset

为什么我在以下代码中遇到编译错误(请参阅注释行)?

    public void Test()
    {
        HashSet<HashSet<Animal>> setWithSets = new HashSet<HashSet<Animal>>();
        HashSet<Cat> cats = new HashSet<Cat>();
        setWithSets.Add(cats); // Compile error
    }

    private class Animal { }

    private class Cat : Animal { }
Run Code Online (Sandbox Code Playgroud)

VS2012给了我两个错误,第一个错误:

  • 错误2参数1:无法从'System.Collections.Generic.HashSet <Expenses.Tests.TestDb.SetTest.Cat>'转换为'System.Collections.Generic.HashSet <Expenses.Tests.TestDb.SetTest.Animal>'
  • 错误1'System.Collections.Generic.HashSet <System.Collections.Generic.HashSet <Expenses.Tests.TestDb.SetTest.Animal >>的最佳重载方法匹配.添加(System.Collections.Generic.HashSet)'有一些无效的参数

我的问题是:为什么我不能在"setWithSets"中添加"cats"?

ken*_*ken 10

为了更好地理解为什么不允许这样做,请考虑以下程序.

该行setOfSets.First().Add(new Dog());是编译器可以接受的,因为动物集合肯定可以保存一个实例Dog.问题是集合中的第一个动物集合是Cat实例的集合,并Dog没有扩展Cat.

class Animal { }
class Cat : Animal { }
class Dog : Animal { }

class Program {
    static void Main(string[] args) {

        // This is a collection of collections of animals.
        HashSet<HashSet<Animal>> setOfSets = new HashSet<HashSet<Animal>>();

        // Here, we add a collection of cats to that collection.
        HashSet<Cat> cats = new HashSet<Cat>();
        setOfSets.Add(cats);

        // And here, we add a dog to the collection of cats. Sorry, kitty!
        setOfSets.First().Add(new Dog());
    }
}
Run Code Online (Sandbox Code Playgroud)


Jep*_*sen 5

即使Cat从导出Animal,这是不正确的,HashSet<Cat>从派生HashSet<Animal>.(唯一的基类HashSet<Anything>object类.)

要获得所需的行为,HashSet<T>泛型类型在其类型参数中需要是协变的T.但事实并非如此,原因有两个:

  1. 在C#中,只有通用接口和通用委托类型可以是共变量或逆变量.HashSet<>是一个班级.
  2. 你不仅可以从a中读取HashSet<>,还可以添加它(并做其他事情).因此,协方差在逻辑上是不可能的.或者,人们可以将a HashSet<Cat>视为a HashSet<Animal>,然后添加一个Dog.但是一组猫不允许狗.

如果您改为HashSet<T>例如IReadOnlyCollection<T>(参见.NET 4.5 文档:IReadOnlyCollection<out T>接口),事情会起作用,因为后一种类型(1)是一个接口,(2)只允许读取,而(3)因此预先标记"我是T"该类型的作者决定适用的协变".


nvo*_*igt 5

您收到编译器错误,因为HashSet的类型构造函数是不变的.

有关术语不变量的解释,请查看协方差和逆变