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

Вопрос новичка: как реализовать общий список 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, чтобы избежать кастинга.

Есть ли какой-то общий шаблон для решения этой проблемы?


person Hans    schedule 26.11.2009    source источник


Ответы (4)


Не на 100% уверен, что понимаю проблему.

Обычно я использую интерфейс с нетипизированным свойством:

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
}

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

person Stefan Steinegger    schedule 26.11.2009

Если вы выполняете некоторые функции с типизированными данными за пределами содержащего класса, вам необходимо заранее знать тип.

Нехорошо:

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);
    }
}
person Leon van der Walt    schedule 30.09.2010

Что-то вроде этого?

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;
}
person nandokakimoto    schedule 26.11.2009

Вот как вы создаете функцию в 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.
}
person this. __curious_geek    schedule 26.11.2009