abstract class Animal { }
class Mammal : Animal { }
class Dog : Mammal { }
class Reptile : Animal { }
class AnimalWrapper<T> where T : Animal
{
public ISet<AnimalWrapper<T>> Children { get; set; }
}
class Program
{
public static void Main(string[] args)
{
var foo = new AnimalWrapper<Mammal>();
foo.Children = new HashSet<AnimalWrapper<Mammal>>();
var child = new AnimalWrapper<Dog>();
foo.Children.Add(child);
}
}
Это, очевидно, не компилируется из-за foo.Children.Add(child);
Как создать общий класс, который содержит набор только своего типа или подтипов в качестве детей?
Я не уверен, если приведенный выше код является наиболее ясно способ продемонстрировать то, что я хочу сделать, поэтому я попытаюсь объяснить на простом English:
Я хочу, чтобы у меня был класс, чьи объекты Children находятся в ISet
того же родового типа. Таким образом, если бы у меня также было var child = new AnimalWrapper<Reptile>();
, то во время компиляции не было бы foo.Children.Add(child);
, потому что Reptile
не является и не наследует от Mammal
. Однако, очевидно, даже если он получен, как показано выше, он не работает.
В конечном счете было бы неплохо говорить ISet<AnimalWrapper<Animal>> baz = new HashSet<AnimalWrapper<Animal>>();
, затем добавить new AnimalWrapper<Mammal>()
к этому набору, а new AnimalWrapper<Reptile>()
- тот же набор. И их дети будут иметь свойство Children
, это ISet<AnimalWrapper<T>>
, где он имеет свой собственный тип, в некотором смысле, как описано выше.
Есть ли способ или я просто ожидаю слишком многого от C#? Черт, я смущаюсь. :)
Edit: Хорошо, так что я почти понял это, без AnimalWrapper
, но с интерфейсом базы IAnimal
, она могла бы почти работа:
interface IAnimal { }
abstract class Animal<T> : IAnimal where T : Animal<T>
{
public ISet<T> Children { get; set; }
}
class Mammal : Animal<Mammal> { }
class Dog : Mammal { }
class Reptile : Animal<Reptile> { }
class Frog : Reptile { }
class Program
{
public static void Main(string[] args)
{
var animals = new HashSet<IAnimal>(); // any animal can be in this
var mammal = new Mammal();
animals.Add(mammal);
mammal.Children = new HashSet<Mammal>();
var dog = new Dog();
mammal.Children.Add(dog); // ok! a dog is a mammal
dog.Children = new HashSet<Dog>(); // in theory, OK, but compile time error
// because Dog : Mammal, and Mammal defines Animal<Mammal>, therefore Dog's
// Children is actually ISet<Mammal>, rather than ISet<Dog> (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.
}
}
Пробовали ли вы кастинг ребенка как «ATypeImpl»? –
Как вы хотите взаимодействовать со структурой данных после ее создания? – dtb
Я думаю, было бы легче понять - и, вполне возможно, увидеть, где он упадет - если вы использовали более конкретные имена. Но я попытаюсь посмотреть позже ... –