Подтвердить что ты не робот

Что эквивалентно шаблонам Java в С# generics

Я разрабатываю приложение, в котором мне нужно вызвать метод универсального класса, и мне не нужен фактический тип экземпляра. Что-то вроде следующего кода Java:

public class Item<T>{
  private T item;

  public doSomething(){...}
}

...
public void processItems(Item<?>[] items){
 for(Item<?> item : items)
   item.doSomething();
}

В то время, когда я торопился, я решил свою проблему, указав интерфейс с методами, которые мне нужно было вызвать, и сделал универсальный класс для его реализации.

public interface IItem  
{
   void doSomething();
}

public class Item<T> : IItem {
  private T item;

  public void doSomething(){...}
}

...
public void processItems(IItem[] items)
{
 foreach(IItem item in items)
   item.doSomething();
}

Это обходное решение работает отлично, но я хотел бы знать, что является правильным способом достижения такого же поведения.

EDIT:

Я забыл сослаться на то, что вызывающий processItems не знает фактические типы. На самом деле идея заключалась в том, что массив, переданный как аргумент processItems, мог содержать смешанные типы. Так как его невозможно получить в .Net, использование не общего базового класса или интерфейса кажется единственным способом.

4b9b3361

Ответ 1

Обычный способ сделать это - сделать общий метод:

public void ProcessItems<T>(Item<T>[] items) {
  foreach(Item<T> item in items)
    item.DoSomething();
}

Предполагая, что вызывающий абонент знает тип, вывод типа должен означать, что им не нужно явно указывать его. Например:

Item<int> items = new Item<int>(); // And then populate...
processor.ProcessItems(items);

Сказав это, может быть полезно также создать не общий интерфейс, определяющий операции типа-агностик. Это будет очень зависеть от вашего конкретного варианта использования.

Ответ 2

Я вижу, что вы только хотите вызвать какой-либо метод без параметров... там уже есть контракт для этого: Action.

public void processItems(IEnumerable<Action> actions)
{
  foreach(Action t in actions)
    t();
}

Клиент:

List<Animal> zoo = GetZoo();
List<Action> thingsToDo = new List<Action>();
//
thingsToDo.AddRange(zoo
  .OfType<Elephant>()
  .Select<Elephant, Action>(e => e.Trumpet));
thingsToDo.AddRange(zoo
  .OfType<Lion>()
  .Select<Lion, Action>(l => l.Roar));
thingsToDo.AddRange(zoo
  .OfType<Monkey>()
  .Select<Monkey, Action>(m => m.ThrowPoo));
//
processItems(thingsToDo);

Ответ 3

Невозможно опустить параметры типа в универсальной реализации .NET; это по дизайну. Фактически, это может быть достигнуто только на Java из-за его реализации на основе стирания.

Вы можете использовать базовый не общий интерфейс (подумайте IEnumerable<T> и IEnumerable).

Ответ 4

В дополнение к почте Джона. что делает общий метод (шаблон) отменяет требование для такого рода функциональных возможностей (с использованием <? > ). Вы всегда можете подавать тип в общий класс/функцию, и в тех случаях, когда вы не знаете, какой тип вам понадобится, вы также можете сделать способ нарушения/универсальный класс... в конечном итоге пользователь должен указать тип при вызове такую ​​функцию или использование универсального класса, чтобы код мог компилироваться... в противном случае вы получите некоторые ошибки компилятора.

Ответ 5

Я боролся с той же проблемой, когда дело доходило до переноса материалов с Java, где у меня были такие конструкции, как

if (o instanceof Collection<?>) doSoemthing((Collection<?>)o);

К счастью, получается, что общий ICollection также является не общим ICollection, и если кому-то нужно рассматривать элементы в нем как чистые объекты, это все еще возможно:

if (o is ICollection) DoSomething((ICollection)o);

Таким образом, поскольку мы не заботимся о фактическом типе элементов в коллекции, мы получаем здесь объекты. Примечание: если коллекция хранит примитивные типы (например, int или byte), то автобоксинговые удары, в которых может возникнуть штраф за производительность.