Типизированный <> / нетипизированный дизайн - PullRequest
4 голосов
/ 06 апреля 2011

У меня есть (существующий) типизированный класс предметов:

Items<T>
    T Value { get; }

T может быть double, string или int.

Затем у меня есть класс, который должен содержать несколько экземпляров Предметов. В пределах одного экземпляра этого класса T всегда одинаков. В сущности, фактически содержащийся тип определяется свойством, а контейнер не набирается:

Data
    DataType { get; set; }
    Items<double>
        double Value;
    Items<string> 
        // ... and so on. Nasty stuff.

В идеале, конечно, это будет

Data<T>
    Items<T>
        T value

Экземпляры данных создаются с нуля в коде и могут быть загружены из базы данных. Поэтому, конечно, фабрика была бы в нашем будущем, но каков тип возврата метода Create?

Еще хуже, мне нужно это:

DataCollection
    // HERE'S THE PAIN: What's the type here?
    List of Data<> instances with differing types

foreach (? data in someDataCollection)
    if (thetypeof data is double)
        doSomething();
    else
        doSomethingElse();

Теперь я могу решить эту проблему, но я не вижу ЧИСТОГО способа решить эту проблему.

Моя первая проблема - объявление DataCollection. Какой тип списка? Список , так что он может содержать данные и данные ?

Ответы [ 2 ]

5 голосов
/ 06 апреля 2011

Существует действительно чистый способ решить эту проблему;Вы можете использовать словарь с ключами типа данных и значениями, которые имеют тип общего Func <>.Затем вы передаете тип в свой метод create, который затем ищет Func <> для использования в Словаре на основе типа и вызывает Func <> для создания или обработки вашего объекта.

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

Сначала создайте родительский класс для всех объектов данных;обратите внимание, что в этом классе есть словарь поиска для функций, которые можно вызывать для различных типов, и обратите внимание, что он абстрактный:

public abstract class Data
{

    // A Lookup dictionary for processing methods
    // Note this the functions just return something of type object; specialize as needed
    private static readonly IDictionary<Type, Func<object, Data>> _processFunctions = new Dictionary
        <Type, Func<object, Data>>()
         {
             {typeof(int), d => { return doSomethingForInt( (Data<int>) d); }},
             {typeof(string), d => { return doSomethingForString( (Data<string>) d); }},
             {typeof(double), d => { return doSomethingForDouble( (Data<double>) d); }},

         };

    // A field indicating the subtype; this will be used for lo
    private readonly Type TypeOfThis;

    protected Data(Type genericType)
    {
        TypeOfThis = genericType;
    }

    public Data Process()
    {
        return _processFunctions[this.TypeOfThis](this);
    }

}

Теперь подкласс Data с универсальным типом, который может быть создан:

class Data<T> : Data
{

    // Set the type on the parent class
    public Data() : base(typeof(T))
    {
    }

    // You can convert this to a collection, etc. as needed
    public T Items { get; set; }

    public static Data<T> CreateData<T>()
    {
        return new Data<T>();
    }
}

Затем вы можете создать класс DataCollection, используя родительский тип.Обратите внимание на метод ProcessData ();все, что он делает сейчас, это циклически перебирает элементы и вызывает Process () для каждого:

class DataCollection
{
    public  IList<Data> List = new List<Data>();

    public void ProcessData()
    {
        foreach (var d in List)
        {
            d.Process();
        }
    }

}

... и все готово!Теперь вы можете вызывать свою коллекцию данных с различными типами данных:

DataCollection dc = new DataCollection();

dc.List.Add(new Data<int>());
dc.List.Add(new Data<string>());
dc.List.Add(new Data<double>());


dc.ProcessData();
1 голос
/ 06 апреля 2011

Я думаю, что каждый раз, когда вам нужно выполнить условия if для типа данных во время выполнения, это означает, что что-то не так со структурами данных. Но каждый раз, когда я сталкиваюсь с такой ситуацией, мне трудно ее решить.

То, что я хотел бы сделать здесь, - это обернуть ваши примитивные типы в некие Адаптеры с помощью методов преобразования (возможно, даже неявных) и заставить их реализовать общий интерфейс, скажем, IDoSomething. Затем вы можете определить поведение doSomething в IntWrapper, DoubleWrapper и т. Д. Отдельно. Тогда ваш DataCollection должен иметь тип List<IDoSomething>, и цикл может просто вызвать метод data.DoSomething() из интерфейса.

Наличие неявного преобразования позволяет вам использовать коллекцию естественным образом, как data.Add(3) - вы все равно сможете добавлять элементы без упаковки привилегий

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...