То же имя свойства, но другой тип возвращаемого значения в универсальном списке
Автор вопроса: HansОтветов: 4
Вопрос новичка: как реализовать общий список List<Item>, возвращающий элементы со свойством с именем Data, но возвращающий разные типы для разных подклассов? Я начал строить следующую иерархию, но это не ведет к цели.
abstract class Item
abstract class ItemGeneric<TData> : Item
class ItemText : ItemGeneric<string>
class ItemImage : ItemGeneric<Image>
Я создаю кучу экземпляров классов ItemText и ItemImage и добавляю их в общий список List<Item>. Однако, как только я просматриваю список и хочу получить свойство Data, оно недоступно, поскольку (очевидно) оно было реализовано только на уровне иерархии класса ItemGeneric<TData>, а не в классе Item.
Я хотел бы решить эту проблему без использования System.Object, чтобы избежать кастинга.
Есть ли какой-то общий шаблон для решения этой проблемы?
Обычно я использую интерфейс с нетипизированным свойством:
interface IUntypedItem
{
object UntypedData {get; }
}
interface IItem<T> : IUntypedItem
{
T Data {get; set;}
}
class abstract ItemGeneric<T> : IItem<T>
{
T Data { get; set; }
object UntypedData { get { return Data; }}
}
class ItemText : ItemGeneric<string>
{
}
Затем вы можете иметь список UntypedItems
List<IUntypedItem> list;
foreach (IUntypedItem item in list)
{
// use item.UntypedData
// or downcast to use typed property
}
Вы не можете избежать приведения или объектов, когда хотите обрабатывать разные типы в одном списке. Вы просто можете прояснить, что вы делаете.
Ответ 2 · рейтинг 1 · Leon van der Walt
Если вы выполняете некоторые функции с типизированными данными за пределами содержащего класса, вам необходимо заранее знать тип.
Нехорошо:
foreach (Item item in list)
{
ItemGeneric<int> intItem = item as ItemGeneric<int>;
if (intItem != null)
{
//do stuff
}
}
Вы можете решить эту проблему, переместив функциональность в класс, содержащий данные.
(см. инкапсуляцию)
Итак, вы можете использовать свой список так:
foreach (Item item in list)
{
item.DoStuff();
}
что означает, что вам понадобится DoStuff в вашем классе Item:
public abstract class Item
{
public abstract void DoStuff();
}
..и может реализовать это на вашем ItemGeneric:
public class ItemGeneric<T> : Item
{
public T Data { get; set; }
public override void DoStuff()
{
//do generic typed stuff here
Console.WriteLine(Data);
}
}
Ответ 3 · nandokakimoto
Что-то вроде этого?
public class ItemGeneric<T> {
private List<T> data;
}
public class ItemText : ItemGeneric<String> { ... }
public class ItemImage : ItemGeneric<Image> { ... }
В Java вы можете определить суперкласс как общий тип T, я не знаю, как это работает в С#.
public class ItemGeneric<T extends IData> {
private List<T> data;
}
Ответ 4 · this. __curious_geek
Вот как вы создаете функцию в BaseClass, которая возвращает коллекцию DerivedTypes при вызове из объекта типа производного класса, никакой магии, только Generics. В этом примере говорится о HashSet, в вашем случае это может быть любой тип, который вы хотите.
//**The BaseClass**
public class BaseClass<T>
where T : BaseClass<T>
{
public HashSet<T> GetHashSet()
{
HashSet<T> hSet = new HashSet<T>();
//create a HashSet<T>
//do some work
//and return;
return hSet;
}
}
//**The Derived Class**
public class DerivedClass : BaseClass<DerivedClass>
{
//you have the method inherited.
}