Возвращает определенный тип в общем методе в зависимости от значения перечисления без отражения или динамического во время выполнения

#c# #generics #dynamic #reflection #runtime

Вопрос:

У меня есть неродовой IList , который я хотел бы использовать в зависимости от enum значения во время выполнения.

Я не могу изменить тип неродового IList в реализации, потому что это код библиотеки.

Также я не хочу использовать отражение (потому что оно медленное) или динамическое ключевое слово (потому что это небезопасно и может привести к ошибкам в будущем).

В коде библиотеки есть следующий класс:

 public class ListView //THIS IS LIBRARY CODE AND CAN NOT BE CHANGED {  public IList itemsSource { get; set; } }  

Затем в моем классе CustomListView, который наследуется от ListView, я хочу привести ItemsSource к соответствующему классу в зависимости от типа элемента.

Еще одним ограничением является то, что CustomListView не может быть универсальным (диктуется используемой нами библиотекой).

 public class CustomListView : ListView {  public dynamic GetItems()  {  switch (ItemType)  {  case ItemType.A:  return itemsSource.Castlt;Agt;().ToList();  case ItemType.B:  return itemsSource.Castlt;Bgt;().ToList();  default:  throw new InvalidOperationException("Not supported");  }  } }  

Но я хочу, чтобы он напрямую возвращал правильный тип вместо использования dynamic.

Что-то вроде (приведенный ниже код не работает!):

 public IListlt;Tgt; GetItems(ItemType itemType) //The type of T should change depending on what i return {  switch (ItemType)  {  case ItemType.A:  return itemsSource.Castlt;Agt;().ToList();  case ItemType.B:  return itemsSource.Castlt;Bgt;().ToList();  default:  throw new InvalidOperationException("Not supported");  } }  

Как бы я это реализовал?

Правка/Добавление

Как вы, ребята, заметили, я должен прояснить некоторые вещи.

Классы A и B действительно имеют один и тот же базовый класс. Но я хочу иметь возможность не приводить его снова из базового типа (потому что я уже привел его в метод GetItems (), а также известно значение ItemType).

Я хочу иметь возможность делать следующее

 IListlt;Agt; aItems = listView.GetItems()  

Без кастинга.

Идея всего этого заключается в том, чтобы иметь общий пользовательский список, который может обрабатывать несколько типов элементов. Эти элементы будут добавлены в ItemSource. Тип этих элементов определяется типом элемента.

Я использую его так, например

 public class UiForClassA {  public void Foo()  {  CustomListView customListView = new CustomListView(ItemType.A);   IListlt;Agt; itemsOfCustomListView = customListView.GetItems(); //No cast needed because it should somehow implicitly know that.  } }  

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

И к вашему сведению, я использую фреймворк Unity UI Toolkit, но на самом деле это не имеет отношения к вопросу.

Комментарии:

1. Если для классов » A » и » B «нет общей функциональности, вам следует вернуть» IListlt;объектgt;». Но у вас, вероятно, должна быть какая-то абстракция, такая как общий интерфейс. Затем вы сможете вернуть » IListlt;CommonInterfacegt;».

2. Вы не можете этого сделать. Именно вызывающий объект задает параметр типа. Вам пришлось бы объявить public IListlt;Tgt; GetItemslt;Tgt;() и назвать его с var list = GetItemslt;Agt;(); помощью . Если GetItems возвращает различные типы списков, как вы хотите их использовать? Потребитель также должен быть универсальным.

Ответ №1:

Подумайте о том, как будет использоваться этот метод:

 ItemType itemType = ...; var x = listView.GetItems(itemType);  

Ценность itemType может быть A , или это может быть B . Итак, каким должен быть этот тип x ? Это либо IListlt;Agt; , либо IListlt;Bgt; , и единственный способ выразить это-использовать тип, от которого оба IListlt;Agt; и IListlt;Bgt; наследуются, что возвращает нас назад IList . Если вы не знаете тип, который вам нужен во время компиляции, то нет особого смысла пытаться изменить возвращаемый тип на что-то более конкретное, потому что вы не сможете получить к нему доступ как к более конкретному типу без размышлений.

Если вы знаете тип, который вам нужен во время компиляции, то предоставьте перегрузки метода, возвращающего нужный тип:

 public IListlt;Agt; GetAItems() { ... } public IListlt;Bgt; GetBItems() { ... }  

Редактировать

Если вы действительно хотите использовать универсальный метод, вы могли бы сделать что-то вроде этого:

 public IListlt;Tgt; GetItemslt;Tgt;() {  return itemsSource.Castlt;Tgt;().ToList(); }  

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

Правка 2

Ознакомившись с вашим вариантом использования, я бы действительно рекомендовал вам сделать CustomListView класс универсальным. Я знаю, вы говорите, что библиотека предотвращает это, но есть способы обойти это. Вы создали ItemType перечисление, вместо этого вы могли бы создать подкласс CustomListViewlt;Tgt; для каждого типа элемента:

 public class CustomListViewlt;Tgt; : ListView {  public IListlt;Tgt; GetItems() =gt; itemsSources.Castlt;Tgt;().ToList(); }  public class CustomListViewOfA : CustomListViewlt;Agt; { } public class CustomListViewOfB : CustomListViewlt;Bgt; { }  public class UiForClassA {  public void Foo()  {  var customListView = new CustomListViewOfA();  var itemsOfCustomListView = customListView.GetItems(); //No cast needed  } }  

Комментарии:

1. Просто я явно задал тип элемента, но все равно должен привести его вручную в определенном коде. Поэтому мне нужен был метод, который неявно возвращает правильный тип в зависимости от типа элемента. Я не хочу помещать оператор Cast в каждое использование этого представления списка, я хочу, чтобы для этого использовался один общий метод.

2. Я все еще не понимаю, зачем вам ItemType вообще нужна переменная. Было бы полезно, если бы вы показали нам, что вы пытаетесь сделать с предметами. Похоже, вы используете библиотеку/фреймворк пользовательского интерфейса — если вы предоставите некоторые сведения о классе, содержащем представление списка, и попытаетесь получить доступ к элементам определенного типа, люди смогут предложить лучшие предложения

Ответ №2:

Как вы предполагаете использовать такой метод? Параметр type задается вызывающим методом. Установка параметра типа с помощью метода невозможна. Пожалуйста, укажите способ использования.

Вы можете попробовать эти:

 IListlt;Tgt; GetItemslt;Tgt;(ItemType itemType) {  switch (itemType)  {  case ItemType.A:  return (IListlt;Tgt;)(IList)(itemsSource.Castlt;Agt;().ToList());  case ItemType.B:  return (IListlt;Tgt;)(IList)(itemsSource.Castlt;Bgt;().ToList());  default:  throw new InvalidOperationException("Not supported");  } }  
 IList GetItems(ItemType itemType) {  switch (itemType)  {  case ItemType.A:  return itemsSource.Castlt;Agt;().ToList();  case ItemType.B:  return itemsSource.Castlt;Bgt;().ToList();  default:  throw new InvalidOperationException("Not supported");  } }  

Или, как предложил @Qwertyluk в комментариях, что-то вроде этого:

 private IListlt;objectgt; GetItems(ItemType itemType) {    switch (itemType)  {  case ItemType.A:  case ItemType.B:  return itemsSource.Castlt;objectgt;().ToList();  default:  throw new InvalidOperationException("Not supported");  } }