How to make generic class that contains a Set of only its own type or subtypes as Children?

后端 未结 2 1333
后悔当初
后悔当初 2020-12-21 03:25
abstract class Animal { }

class Mammal : Animal { }

class Dog : Mammal { }

class Reptile : Animal { }

class AnimalWrapper where T : Animal
{
    public          


        
相关标签:
2条回答
  • 2020-12-21 04:12

    The main problem is, a bit oversimplified, in covariance upcasting (and contravariance with the ISet)

    Try it this way...

    abstract class Animal { }
    class Mammal : Animal { }
    class Dog : Mammal { }
    class Reptile : Animal { }
    
    interface INode<out T> where T : Animal
    {
        T MySelf { get; }
        IEnumerable<INode<T>> Children { get; }
    }
    
    class Node<T> : INode<T>
        where T : Animal
    {
        public Node() { this.Children = new HashSet<INode<T>>(); }
        public T MySelf { get; set; }
        public ISet<INode<T>> Children { get; set; }
        IEnumerable<INode<T>> INode<T>.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<Mammal> tree = new Node<Mammal>();
            tree.MySelf = new Mammal();
    
            var node1 = new Node<Mammal>();
            tree.Children.Add(node1);
    
            var node2 = new Node<Dog>();
            tree.Children.Add(node2);
    
            var node3 = new Node<Reptile>();
            // tree.Children.Add(node3); // this fails to compile
    
    
            // ...and similar just more 'open' - if you 'collect' animals, all are welcome
    
            Node<Animal> animals = new Node<Animal>();
            animals.MySelf = new Mammal();
    
            INode<Mammal> mamals = new Node<Mammal>();
            animals.Children.Add(mamals);
    
            var dogs = new Node<Dog>();
            animals.Children.Add(dogs);
    
            INode<Animal> reptiles = new Node<Reptile>();
            animals.Children.Add(reptiles);
        }
    }
    

    (look up the comments)

    This doesn't mean it'd work in your real-life case - as this requires some 'design refactoring' to keep it working with a more complex structure (if possible).

    ...just fast, I'll try to explain some more later if needed

    0 讨论(0)
  • 2020-12-21 04:16

    This happens because when you instantiate an instance of AnimalWrapper<T> using the generic type argument Mammal, the Children member will be of type ISet<AnimalWrapper<Mammal>> and not of type ISet<AnimalWrapper<Dog>>. Hence the reason you can't add an instance of AnimalWrapper<Dog>to the generic collection.

    One possible way I see you could address this might be if you were to implement an interface.

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

    Then you will need to change the way you instantiate the Children collection...

    foo.Children = new HashSet<IAnimalWrapper>();
    

    Now you can add to the different types of children...

    foo.Children.Add(new AnimalWrapper<Mammal>());
    foo.Children.Add(new AnimalWrapper<Dog>());
    foo.Children.Add(new AnimalWrapper<Reptile>());
    

    So that will get it to compile but I am still curious as to why you really need the generic class (AnimalWrapper<T>). I imagine there could be reasons for it but maybe just doing away with that type would simplify things (depending on the larger context)...

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

    In other words, just rely ISet<T> alone to provide the type...

    var foo = new Mammal();
    foo.Children = new HashSet<AnimalWithChildren>();
    foo.Children.Add(new Mammal());
    foo.Children.Add(new Dog());
    foo.Children.Add(new Reptile());
    
    0 讨论(0)
提交回复
热议问题