2011-04-06 93 views
4

我有一个(现有的)类型类项:类型化<> /无类型设计

Items<T> 
    T Value { get; } 

T可以是双,字符串或者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的声明。列表的类型是什么?名单<对象>,所以它可以容纳数据<双>和数据<字符串>?

+1

请出示实际的代码,而不是伪代码。 – 2011-04-06 16:49:48

回答

5

实际上有一个干净的方法来解决这个问题;您可以使用带有数据类型和值的键的字典,类型为泛型Func <>。然后,将该类型传递给create方法,然后该方法根据类型查找Func <>以在Dictionary中使用,并调用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); 
    } 

} 

现在子类数据与泛型类型可实例:

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(); 
+0

不错,但我还是一个级别:DataCollection是什么样的? – Stu 2011-04-06 17:19:02

+0

更具体地说,我需要一个包含“Data ”和“Data ”的集合。也就是说,集合**没有T **。 – Stu 2011-04-06 17:24:32

+0

如果我做了'Data fooData = Data.CreateData ()'后面跟着'Data.Process(fooData);' – 2011-04-06 17:48:45

1

我认为每一个你需要做的时间,如果条件运行时的数据类型,这意味着有一些错误的数据结构。但是每次遇到这样的情况,我都很难解决这个问题。

我想在这里做的是将你的原始类型转换为某种具有转换方法的适配器(甚至可能是隐式的),并让它们都实现一个通用接口,比如说​​。然后,您可以分别在IntWrapperDoubleWrapper等中定义doSomething行为。那么你的DataCollection应该是List<IDoSomething>类型,并且循环可以从接口调用data.DoSomething()方法。

有隐式转换,您可以使用集合像data.Add(3)自然的方式 - 你仍然可以添加项目,而不包裹privitives

+0

'data.add(3)'不起作用。你会得到'Argument'1'的编译错误:不能从'int'转换为'IDoSomething''你仍然需要'data.Add(IntWrapper(3))' – 2011-04-06 18:16:26

相关问题