开发者

Typed<>/untyped design

开发者 https://www.devze.com 2023-02-22 06:53 出处:网络
I have an (existing) typed class of items: Items<T> T Value { get; } T can be double, string or int.

I have an (existing) typed class of items:

Items<T>
    T Value { get; }

T can be double, string or int.

I then have a class that has to hold several instances of Items. Within a single instance of this class, T is always the same. As it stands, the type actually contained is determined by a property and the container is not typed:

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

Ideally, of course, this would be

Data<T>
    Items<T>
        T value

Data instances are created from scratch in code, and can be loaded from a database. So of course a factory would be in our future, but what is the return type of the Create method?

Even worse, I need this:

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

Now, I can solve this, but I can't see a CLEAN way to solve this.

My first issue is the declaration of DataCollection. What is the type of the list? List<object>, so it can hold Data<double> and Data<string>?


There actually is a clean way to solve this; you can use a Dictionary with keys of the data type and values which are of type generic Func<> . You then pass the type to your create method, which then looks up the Func<> to use in the Dictionary based on the type, and invokes that Func<> to create or process your object.

Since I am working from pseudo code, basically it would look something like the below; you can play with it and modify it to get it to serve your needs, but this is the basic idea.

First, create a parent class for all data objects; note that this class has a lookup dictionary for functions to invoke on various types, and note that it is abstract:

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

}

Now subclass Data with a generic type that can be instantiated:

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

You can then create a DataCollection class using the parent type. Note the ProcessData() method; all it does now is loop over the elements and call Process() on each one:

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

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

}

...and you're all set! Now you can invoke your DataCollection with different types of Data:

DataCollection dc = new DataCollection();

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


dc.ProcessData();


I think every time you need to do if-conditions on runtime data type, it means there's something wrong with the data structures. But every time I encounter situation like this, I have a hard time to solve it.

What I would try to do here is to wrap your primitive types into some kind of Adapters with conversion methods (possibly even implicit) and make all of them implement a common interface, let's say IDoSomething. Then, you can define the doSomething behaviors in IntWrapper, DoubleWrapper etc. separately. Then your DataCollection should be of type List<IDoSomething> and the loop can just call data.DoSomething() method from the interface.

Having implicit conversion allows you to use the collection in the natural way like data.Add(3) - you'll still be able to add the items without wrapping the privitives

0

精彩评论

暂无评论...
验证码 换一张
取 消