Jan
Jan

Reputation: 2168

Generic collection in generic class

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

Answers (4)

metalhead
metalhead

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

Daniel Hilgarth
Daniel Hilgarth

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

mathk
mathk

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

Clint
Clint

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

Related Questions