Вы не можете сделать то же самое, что и Java, потому что в Java генерики используют стирание типа и разрыв дисперсии. По сути, ваш код Java превращает все в List<object>
и надеется на лучшее. Единственная причина, по которой List<?>
лучше, чем List<Object>
, заключается в том, что не все является Object
в Java - в C#, вы можете поместить целое число в List<object>
просто отлично. Имейте в виду, что List<int>
будет работать намного лучше, чем List<object>
, если вы можете себе это позволить - это одна из основных причин, почему дженерики были изначально добавлены в C#.
C# немного строже чем это. Вы не можете сделать ничего подобного new List<Parent<T>>()
, которое бы разрешало любой вид Parent<T>
. Если бы у вас были более ограниченные требования, вы могли бы вместо этого использовать вариантный интерфейс, но он не работал бы с List<T>
по понятным причинам.
Единственный реальный вариант - сделать базовый класс неиспользуемым * 1042. *. Пользователь вашего списка в любом случае не может ничего знать о T
заранее, поэтому любая часть интерфейса Parent
, которая возвращает или принимает T
, была бы бесполезна без приведения (в любом случае Java выполняет приведение для вас, но он все еще работает - ни Java, ни C# дженерики не являются достаточно мощными для того, что вы пытаетесь сделать).
public abstract class Parent
{
// The common methods
public abstract int Id { get; }
}
public abstract class Parent<TChild> : Parent, IEnumerable<TChild>
{
// The methods that are TChild-specific - if you don't need any of those, just drop
// this class, the non-generic one will work fine
private List<TChild> children;
public void Add(TChild child) => ...
public TChild this[int index] => ...
}
public class Child : Parent<TChild>
{
...
}
Теперь, чтобы получить список всех возможные дети, вы можете использовать
var list = new List<Parent>();
И когда вам нужно получить, например, все Child
предметы, вы можете сделать
var children = list.OfType<Child>();
Просто для полноты вы можете получить аналогичные поведение Java с C# dynamic
. Но я даже не собираюсь показывать какой-либо пример этого - dynamic
- полезный инструмент, но в основном для более динамичных проблем c печати. Это излишне для чего-то такого простого и обменивает проблемы времени компиляции на проблемы времени выполнения.
В общем, если вы когда-либо используете Parent<T>
напрямую, это должно быть в общем методе c - например, метод расширения, который имеет некоторые общие функции для всех Parent<T>
с. Вы не можете создать экземпляр обобщенного типа c, который не имеет всех аргументов типа, известных в то время в C#.