如何创建包含一组只有自己的类型或子类型为子类的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
不是也不是从Mammal
inheritance而来。 但是,显然,即使它是派生的,如上所示,它也不起作用。
最终,能够说出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());