Reputation: 2168
I have a custom class (let's call it MyClass
) that looks like this:
public class MyClass
{
private List<MyClass> list;
private object data;
}
However, I want to get rid of the object
property and instead use a generic class. So something like this:
public class MyClass<T>
{
private List<MyClass<T>> list;
private T data;
}
However, I am in need for this behavior:
MyClass<Foo> foo = new MyClass<Foo>;
foo.list = new List<MyClass<Bar>>;
So I need to be able to have different datatypes for the foo-instance and the list/data-property in foo. But the T's in the generic example will be the same and only allow this:
MyClass<Foo> foo = new MyClass<Foo>;
foo.list = new List<MyClass<Foo>>;
Each item in foo.list will again have a list that might be of a different type. By the time I compile MyClass
I have no knowledge about what datatypes will be in the lists/data-property or how many levels there will be. How can I build this flexible structure?
Upvotes: 2
Views: 162
Reputation: 567
I am trying to implement a very similar thing in C++ right now and am running into the same problem. The problem with templates is that they are strongly typed and MyClass<foo>
is treated as a complete different and unrelated type as MyClass<bar>
.
What I am playing with now is creating an abstract class that has pure virtual methods on it like GetInt
, GetDouble
, GetBool
, GetString
. I then want to have a templated Add
that will instantiate the appropriate concrete class and add it to my vector. I'm not sure if it will work, but it's along these lines:
class Data
{
public:
template<typename T> Add(const std::string& key, const T& val)
{
Data* pData = NULL;
//test the type using std::numeric_limits to figure out which concrete
//type to use.
m_mapValues[key] = pData;
}
protected:
virtual int GetInt() const = 0;
virtual unsigned int GetUINT() const = 0;
virtual std::string GetString() const = 0;
//blah blah more types that you want to handle
private:
std::map<std::string,Data*> m_mapValues;
};
class UINTData : public Data
{
//implement pure virtual methods.
}
This is obviously incomplete, but I hope it gives you some ideas.
Upvotes: 0
Reputation: 174289
You can't achieve this goal with just one class. You will have to build one generic base class for each level with children, one base class for the lowest level without children and one derived class per hierarchy level.
The base classes would look like this:
public class MyClass<TData>
{
public TData Data { get; set; }
}
public class MyClass<TData, TDataChildren, TChildren> : MyClass<TData>
where TChildren : MyClass<TDataChildren>
{
public List<TChildren> List { get; set; }
}
The derived classes per Level would look like this:
public class Level0 : MyClass<Foo, Bar, Level1> { }
public class Level1 : MyClass<Bar, Fubar, Level2> { }
public class Level2 : MyClass<Fubar> { }
Usage:
var root = new Level0();
root.Data = new Foo();
root.List = new List<Level1>
{
new Level1()
{
Data = new Bar(),
List = new List<Level2>
{
new Level2()
{
Data = new Fubar()
}
}
}
};
Upvotes: 1
Reputation: 8123
Add a second type in MyClass.
public class MyClass<T, G>
{
private List<MyClass<G, T>> list;
private T data;
}
This is assuming that you don't care the type of the nested list inside your list.
But can you elaborate a bit more on the utility of your code because it is hard to tel just with abstract data.
Upvotes: 0
Reputation: 6220
Generics are designed to allow for the compiler to perform checks on type usage and they also provide some nifty additional benefits.
What you've described cannot be achieved with generics, if each time you're updating list
with a list of a potentially different type then generics cannot help you.
However, if each of these types share a common base type or share a common interface then you could use that as your T
for the list and that would allow you to use them.
If however each instance of MyClass
allows only one type of List of MyClass<?>
then you could revise MyClass
as such:
public class MyClass<T, TList>
{
private List<MyClass<T, TList>> list;
private T data;
}
Upvotes: 1