如何创建包含一组只有自己的类型或子类型为子类的generics类?

abstract class Animal { } class Mammal : Animal { } class Dog : Mammal { } class Reptile : Animal { } class AnimalWrapper where T : Animal { public ISet<AnimalWrapper> Children { get; set; } } class Program { public static void Main(string[] args) { var foo = new AnimalWrapper(); foo.Children = new HashSet<AnimalWrapper>(); var child = new AnimalWrapper(); foo.Children.Add(child); } } 

由于foo.Children.Add(child);这显然无法编译foo.Children.Add(child);

我不确定上面的代码是否是展示我想要做的最明确的方式,所以我将尝试用简单的英语解释:

我希望能够拥有一个类,其Children对象位于同一generics类型的ISet中。 因此,如果我也有var child = new AnimalWrapper(); 它会在编译时无法执行foo.Children.Add(child); 因为Reptile不是也不是从Mammalinheritance而来。 但是,显然,即使它是派生的,如上所示,它也不起作用。

最终,能够说出ISet<AnimalWrapper> baz = new HashSet<AnimalWrapper>(); 然后将new AnimalWrapper()到该集合,并将new AnimalWrapper()到同一集合中。 ISet<AnimalWrapper> ,他们的孩子将拥有一个属于ISet<AnimalWrapper> Children ,在ISet<AnimalWrapper> ,它属于自己的类型。

有没有办法,或者我只是期待C#过多? 哎我自己很困惑。 🙂

编辑:好的,所以我几乎想到了这一点,没有AnimalWrapper ,但是有了一个基本的IAnimal接口,它几乎可以工作:

 interface IAnimal { } abstract class Animal : IAnimal where T : Animal { public ISet Children { get; set; } } class Mammal : Animal { } class Dog : Mammal { } class Reptile : Animal { } class Frog : Reptile { } class Program { public static void Main(string[] args) { var animals = new HashSet(); // any animal can be in this var mammal = new Mammal(); animals.Add(mammal); mammal.Children = new HashSet(); var dog = new Dog(); mammal.Children.Add(dog); // ok! a dog is a mammal dog.Children = new HashSet(); // in theory, OK, but compile time error // because Dog : Mammal, and Mammal defines Animal, therefore Dog's // Children is actually ISet, rather than ISet (which is what // I want, recursively apply the T in Animal. Mammal mammal2 = new Mammal(); dog.Children.Add(mammal2); // should be verboten, but is allowed for the // same reason above. } } 

主要问题是,在协方差向上翻译中有点过于简单(与ISet相反)

试试这种方式……

 abstract class Animal { } class Mammal : Animal { } class Dog : Mammal { } class Reptile : Animal { } interface INode where T : Animal { T MySelf { get; } IEnumerable> Children { get; } } class Node : INode where T : Animal { public Node() { this.Children = new HashSet>(); } public T MySelf { get; set; } public ISet> Children { get; set; } IEnumerable> INode.Children { get { return this.Children; } } } class Program { static void Main(string[] args) { // this is a 'typical' setup - to test compiler 'denial' for the Reptile type... Node tree = new Node(); tree.MySelf = new Mammal(); var node1 = new Node(); tree.Children.Add(node1); var node2 = new Node(); tree.Children.Add(node2); var node3 = new Node(); // tree.Children.Add(node3); // this fails to compile // ...and similar just more 'open' - if you 'collect' animals, all are welcome Node animals = new Node(); animals.MySelf = new Mammal(); INode mamals = new Node(); animals.Children.Add(mamals); var dogs = new Node(); animals.Children.Add(dogs); INode reptiles = new Node(); animals.Children.Add(reptiles); } } 

(查看评论)

这并不意味着它在你的现实生活中起作用 – 因为这需要一些“设计重构”来保持它与更复杂的结构(如果可能的话)一起工作。

……快点,如果需要,我会稍后再解释一下

这是因为当您使用generics类型参数Mammal实例化AnimalWrapper的实例时, Children成员将是ISet>类型而不是ISet> 。 因此,您无法将AnimalWrapper的实例添加到generics集合中。

我看到你可以解决这个问题的一种可能方法是,如果你要实现一个接口。

 interface IAnimalWrapper { } class AnimalWrapper : IAnimalWrapper where T : Animal { public ISet Children { get; set; } } 

然后,您将需要更改实例化Children集合的方式…

 foo.Children = new HashSet(); 

现在你可以添加到不同类型的孩子……

 foo.Children.Add(new AnimalWrapper()); foo.Children.Add(new AnimalWrapper()); foo.Children.Add(new AnimalWrapper()); 

所以这将使它编译,但我仍然很好奇为什么你真的需要generics类( AnimalWrapper )。 我想可能有理由,但也许只是取消这种类型会简化事情(取决于更大的背景)……

 abstract class AnimalWithChildren { public ISet Children { get; set; } } class Mammal : AnimalWithChildren { } class Dog : Mammal { } class Reptile : AnimalWithChildren { } 

换句话说,只需依靠ISet来提供类型……

 var foo = new Mammal(); foo.Children = new HashSet(); foo.Children.Add(new Mammal()); foo.Children.Add(new Dog()); foo.Children.Add(new Reptile());