键入<> /非类型设计

时间:2011-04-06 16:41:31

标签: c# oop design-patterns generics

我有一个(现有的)类型的项目:

Items<T>
    T Value { get; }

T可以是double,string或int。

然后我有一个必须持有几个Items实例的类。在这个类的单个实例中,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的声明。列表的类型是什么?列出&lt; object&gt;,因此它可以保存Data&lt; double&gt;和数据&lt; string&gt;?

2 个答案:

答案 0 :(得分:5)

实际上有一种干净的方法来解决这个问题;您可以使用具有数据类型的键的字典和具有通用类型Func&lt;&gt;类型的值。 。然后将类型传递给create方法,然后查找Func&lt;&gt;根据类型在Dictionary中使用,并调用Func&lt;&gt;创建或处理您的对象。

由于我使用伪代码,基本上它看起来像下面的内容;你可以玩它并修改它以满足你的需求,但这是基本的想法。

首先,为所有数据对象创建父类;请注意,此类有一个查找字典,用于调用各种类型的函数,并注意它是抽象的:

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);
    }

}

现在使用可以实例化的泛型类型子类化数据:

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:

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 :(得分:1)

我认为每次需要对运行时数据类型执行if条件时,都意味着数据结构出现了问题。但每当遇到这样的情况时,我都很难解决它。

我在这里尝试做的是将原始类型包装到某种具有转换方法的适配器中(可能甚至是隐式的),并使它们全部实现一个通用接口,比方说IDoSomething。然后,您可以分别在doSomethingIntWrapper等中定义DoubleWrapper行为。然后,您的DataCollection应为List<IDoSomething>类型,循环只能从界面调用data.DoSomething()方法。

通过隐式转换,您可以像data.Add(3)这样以自然的方式使用集合 - 您仍然可以在不包含权限的情况下添加项目