Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C# Generic List of Generic List of Multiple Types

Tags:

c#

generics

Here is an abstraction and simplification of my issue:

I have a set of toys and a corresponding box for these toys. I want the user to be able to specify the largest type of toy that the box can hold:

public class Box<T> {}

then within the Box class I want to have a generic list of toys, but each toy contained within the box will have a generic type:

public class Box<T>
{
    public List<Toy> = new List<Toy>();
    public bool Whatever;

    [member functions, constructors...]
    [The member functions will depend on T]
}

The Toys class will look like this:

public class Toy<T> where T : struct //T is any type
{
    public List<T> = new List<T>();
    public string Name;
    public string Color;

    [member functions, constructors...]
}

I want to be able to create Toys with many different types and then insert them into a Box with another specified type. Then I'd like to be able to add boxes together returning a Box with the largest type.

I really don't know how to begin. The list of a generic class with multiple types is really throwing me for a loop. I read various articles about using an abstract class or an interface, but haven't found an example or anything that accomplishes something similar to what I'm trying to do.

Any assistance anybody could provide would be very appreciated.

The solution can be in C# 4.0.

Possible Future Clarification:

I want Toy to be generic and accept a argument at instantiation because Toy must also have a List as a member.

The nested List within Toy is my main problem. I then want a list within Box that holds Toys, but each toy has as different type constructor.

Update:

I fixed the Box to Box that was a typo.

Update 2:

Toy<plastic> tyPlastic = new Toy<plastic>("Name1", Blue, new plastic[] {0xFFEE00, 0xF34684, 0xAA35B2});
Toy<wood> tyWood = new Toy<wood>("Name2", Grain, new wood[] {a1, f4, h7});

Box<plastic> bxBox = new Box<plastic>();//The Box has the ability to hold both plastic and wood toys.  Plastic > Wood > Paper

Final: I ended up removing the requirement for Box to be generic. I then used reflection to create dynamically typed Toy. Thanks everybody.

like image 595
Chris Avatar asked Jan 16 '10 01:01

Chris


1 Answers

The code you're building will be best understood if it models reality well.

The way to model "an A of B" is to use generics. A set of kinds of box that can hold one kind of thing would be modelled as Box<T>. A box that can only hold toys would be Box<Toy>. A set of kinds of box that can hold one kind of thing, and that thing has to be a toy would be a Box<T> where T : Toy.

So far so good. But the concept of Toy<T> doesn't map to anything in real life. You might have a box of biscuits or a box of toys, but you don't have a toy of biscuits, a toy of dolls or a toy of giraffes. The concept "toy of" doesn't make any sense, so don't model it.

A more sensible thing to model would be "there is a general class of things called toys. There is no one thing that is just a toy; every toy is a more specific kind of toy. A ball is a toy. A doll is a toy." So model that:

abstract class Toy {}
class Doll : Toy {}
class Ball : Toy {}

You said

I want Toy to be generic and accept a argument at instantiation because Toy must also have a List as a member.

Why? A toy does not have a list of things. So don't model that. Rather, a box is logically modelled as a list of the toys that are inside the box. (Or, since a box does not generally apply an ordering, and a box contains only unique toys, perhaps a set of toys would be better.)

I want to be able to create Toys with many different types and then insert them into a Box with another specified type.

OK. So an operation on Box<T> is void Insert(T item). You can put a toy into a box of toys, you can put a doll into a box of dolls, but you cannot put a ball into a box of dolls.

Then I'd like to be able to add boxes together returning a Box with the largest type.

You need to more carefully define "the largest type". If you add a box of dolls to a box of balls, clearly the result is neither a box of balls nor a box of dolls. The result is a box of toys.

Here's how I would model this. We already have the toy hierarchy. I would continue by saying that a box of T is implemented as a set of its contents, and provides a sequence of its contents.

// Haven't actually compiled this.
class Box<T> : IEnumerable<T>
{
    private HashSet<T> set = new HashSet<T>();
    public Insert(T item) { set.Add(item); }
    public IEnumerator<T> GetEnumerator() { return set.GetEnumerator(); }
    public IEnumerator IEnumerable.GetEnumerator() { return this.GetEnumerator(); }

All very boring so far. Now we come to the interesting bit. This only works well in C# 4.

    public static Box<T> MergeBoxes(IEnumerable<T> box1, IEnumerable<T> box2)
    {
        Box<T> box = new Box<T>();
        foreach(T item in box1) box.Insert(item);
        foreach(T item in box2) box.Insert(item);
        return box;
    }
}

Now you can say

Box<Doll> dollbox = new Box<Doll>() { new Doll() };
Box<Ball> ballbox = new Box<Ball>() { new Ball() };
Box<Toy> toybox2 = Box<Toy>.MergeBoxes(ballbox, dollbox);

The result of merging a box of dolls with a box of balls is a box of toys.

This last bit only works because IEnumerable<T> is covariant in C# 4. In C# 3, this would be trickier to get right; you'd have to do something like:

Box<Toy> toybox2 = Box<Toy>.MergeBoxes(ballbox.Cast<Toy>(), dollbox.Cast<Toy>());

Does that make sense?

like image 198
Eric Lippert Avatar answered Sep 17 '22 12:09

Eric Lippert