C # эквивалент Java Список - PullRequest
       19

C # эквивалент Java Список

3 голосов
/ 21 февраля 2020

У меня есть базовая c структура классов generi c

public class Parent<T> where T : Parent<T>
{
   Action<T> Notify;
}

public class Child : Parent<Child>
{
}

И я хочу иметь список, чтобы туда могли помещаться дочерние объекты

List<Parent> parents = new List<Parent>();

В java я просто могу написать List<? extends Parent>, и все подклассы Родителя легко могут быть добавлены в список. Есть ли альтернатива этому в C#?

Ответы [ 2 ]

2 голосов
/ 21 февраля 2020

Объявление List<Parent> parent; не компилируется, поскольку требует аргумента типа.

И когда вы говорите, public class Child : Parent<Child> он наследует Parent<Child>, а не Parent<T>

Так что List<Parent<Child>> list; будет принимать только объекты класса Child, но не любого другого подкласса Parent ,

Тем не менее, вы можете достичь того, что вам нужно, с помощью интерфейса, как показано ниже: рабочая скрипка здесь

public class Program
{
    public static void Main()
    {
        List<Parent<IParent>> parentList = new List<Parent<IParent>>();

        parentList.Add(new Child1());
        parentList.Add(new Child2());       
    }
}
public class Parent<T> 
{ }
public interface IParent
{ }

public class Child1 : Parent<IParent>, IParent
{ }

public class Child2 : Parent<IParent>, IParent
{ }
2 голосов
/ 21 февраля 2020

Вы не можете сделать то же самое, что и 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#.

...