Typical <> / untyped design

I have a (existing) typed element class:

 Items<T> T Value { get; } 

T can be double, string or int.

Then I have a class that should contain multiple instances of Items. In one instance of this class, T is always the same. Be that as it may, the actually contained type is determined by the 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, that would be

 Data<T> Items<T> T value 

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

Worse, I need this:

 DataCollection // HERE THE PAIN: What 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 problem, but I can’t see a CLEAN way to solve this problem.

My first problem is declaring a DataCollection. What is the type of list? List <object>, so it can contain data <double> and Data <string>?

+4
source share
2 answers

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

Since I work from pseudo-code, basically it will look something like this: you can play with it and modify it so that it can satisfy your needs, but this is the main idea.

First create a parent class for all data objects; note that this class has a search dictionary for functions called on different 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 a subclass of Data with a typical type that can be created:

 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 the DataCollection class using the parent type. Pay attention to the ProcessData () method; all he does now is loop over the elements and call Process () for each of them:

 class DataCollection { public IList<Data> List = new List<Data>(); public void ProcessData() { foreach (var d in List) { d.Process(); } } } 

... and you're done! Now you can call your DataCollection with various data types:

 DataCollection dc = new DataCollection(); dc.List.Add(new Data<int>()); dc.List.Add(new Data<string>()); dc.List.Add(new Data<double>()); dc.ProcessData(); 
+5
source

I think that every time you need to make if-conditions on a runtime data type, it means that something is wrong with the data structures. But every time I encounter a similar situation, it is difficult for me to solve it.

What I would like to do here is to wrap your primitive types in some kind of adapter using conversion methods (possibly even implicit) and force all of them to implement a common interface, say IDoSomething . Then you can define the behavior of doSomething in IntWrapper , DoubleWrapper , etc. Separately. Then your DataCollection should be of type List<IDoSomething> , and the loop can simply call the data.DoSomething() method from the interface.

The presence of an implicit conversion allows you to use the collection in a natural way, for example data.Add(3) - you can still add items without packing privileges

+1
source

Source: https://habr.com/ru/post/1346952/


All Articles