C#多种类型通用列表的通用列表

时间:2010-01-16 01:36:43

标签: c# generics

以下是我的问题的抽象和简化:

我有一套玩具和相应的玩具箱。我希望用户能够指定盒子可容纳的最大类型的玩具:

public class Box<T> {}

然后在Box类中我想要一个通用的玩具列表,但是盒子中包含的每个玩具都有一个通用类型:

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

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

Toys类看起来像这样:

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...]
}

我希望能够创建具有许多不同类型的Toys,然后将它们插入到具有其他指定类型的Box中。然后,我希望能够一起添加方框,返回最大类型的Box。

我真的不知道该如何开始。具有多种类型的泛型类的列表实际上是让我循环。我阅读了有关使用抽象类或接口的各种文章,但没有找到一个例子或任何可以完成类似于我正在尝试的事情的文章。

非常感谢任何人提供的任何帮助。

解决方案可以在C#4.0中。

可能的未来澄清:

我希望玩具是通用的,并在实例化时接受参数,因为玩具必须还有一个列表作为成员。

玩具中的嵌套列表是我的主要问题。然后我想要一个包含玩具的Box中的列表,但每个玩具都有不同的类型构造函数。

更新

我将Box修改为拼写错误的Box。

更新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

最后:我最终删除了Box为通用的要求。然后我用反射来创建动态类型的玩具。谢谢大家。

5 个答案:

答案 0 :(得分:24)

如果能够很好地模拟现实,那么您正在构建的代码将被最好地理解。

模拟“A of B”的方法是使用泛型。一组可以容纳一种东西的盒子将被建模为Box<T>。只能装玩具的盒子是Box<Toy>。一套可以容纳某种东西的盒子,那件东西必须是玩具Box<T> where T : Toy

到目前为止一切顺利。但是Toy<T>的概念并没有映射到现实生活中的任何东西。你可能有一盒饼干或一盒玩具,但你没有饼干玩具,娃娃玩具或长颈鹿玩具。 “玩具”这个概念没有任何意义,所以不要对它进行建模

一个更明智的建模方法是“有一类通用的东西叫做玩具。没有一件东西只是一个玩具;每一件玩具都是一种更特殊的玩具。一个球就是一个玩具。娃娃是玩具。“所以建模:

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

你说

  

我希望玩具是通用的,并在实例化时接受参数,因为玩具必须还有一个列表作为成员。

为什么呢? 玩具没有物品清单。所以不要模仿那个。相反,盒子在逻辑上被建模为盒子内的玩具列表。 (或者,因为一个盒子通常不会应用订购,并且一个盒子只包含独特的玩具,所以玩具的 set 会更好。)

  

我希望能够创建具有许多不同类型的Toys,然后将它们插入到具有其他指定类型的Box中。

行。因此,Box<T>上的操作是void Insert(T item)。你可以将一个玩具放入一盒玩具中,你可以将一个玩偶放入一盒娃娃中,但你不能把球放入一盒娃娃中。

  

然后我希望能够一起添加方框,返回最大类型的Box。

您需要更仔细地定义“最大类型”。如果你在一盒球上添加一盒娃娃,结果显然结果既不是一盒球也不是一盒娃娃。结果是一盒玩具。

以下是我对此进行建模的方法。我们已经拥有了玩具等级。我会继续说一个T盒被实现为一组内容,并提供一系列内容。

// 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(); }
到目前为止,一切都很无聊。现在我们来到有趣的位置。 这只适用于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;
    }
}

现在你可以说

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

将一盒娃娃与一盒球合并的结果是一盒玩具。

这最后一位只能起作用,因为IEnumerable<T>在C#4中是协变的。在C#3中,这样做是很难的。你必须做类似的事情:

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

这有意义吗?

答案 1 :(得分:1)

我不确定这是否是你真正想做的事情,因为它有点像hackish。如果你想要所有可以想象的玩具类型的列表,包括Box可能不知道的那些,你将需要使用你的通用参数来过滤掉正确的玩具。我不建议这样做,除非Box只是填充常见内容的空白(例如,BlackBoard)。

例如,您可以只有一个List&lt; Object&gt;并且所有方法在操作之前检查内容是否是正确的类型。您还可以使用Dictionary&lt; Type,Object&gt;为了跳过逐项过滤步骤,但导致一些遗传问题(动物列表不包括添加到长颈鹿列表中的东西)。

以下是Dictionary方法的简单实现:

public class MultiGenericList
{
    private readonly Dictionary<Type, Object> map = new Dictionary<Type,Object>();

    public List<T> GetList<T>()
    {
        if (!this.map.ContainsKey(typeof(T))) this.map.Add(typeof(T), new List<T>());
        return (List<T>)this.map[typeof(T)];
    }
}

您可以调整此代码以将列表限制为特定类型,并且您可以进一步调整它以便您可以加入共享公共基本类型的列表。这可能是这样的:

MultiGenericList<T> Join<C1, C2, T>(MultiGenericList<C1> child1, 
                                    MultiGenericList<C2> child2) where C1 : T, C2 : T
{
    ...
}

答案 2 :(得分:1)

我引用MSDN:

  

通用类封装操作   这不是特定的   数据类型。最常见的用途   泛型类与集合有关   像链表,哈希表,   堆栈,队列,树木等等   添加和删​​除等操作   该系列中的物品是   以同样的方式表演   无论数据类型如何   存储

根据你的描述,最适合的对象是Box,但是你知道它将包含Toys,并且有一个最大的ToySize,并且玩具有一个类型(Ball,Horse .. )。我可能在这里错了,但是使用泛型可能并不是必需的。我想的是:

    public class Box
    {
        public ToyType MaxType { get; set; }
        public List<Toy> Toys = new List<Toy>();

        public void Add(Toy toy)
        {
            if (toy.Type.Size <= MaxType.Size) //if its not larger, or whatever compatibility test you want
                Toys.Add(toy);
        }
    }

    public class Toy
    {
        public ToyType Type { get; set; }
        public Toy(ToyType type)
        {
            Type = type;
        }  
    }

    public abstract class ToyType
    {
        public abstract string TypeName { get; set; }
        public abstract int Size { get; set; }
    }

除非你有使用泛型的特殊原因。

答案 3 :(得分:1)

在构建泛型类型列表时我遇到了这个问题,我不知道编译时的通用部分,它们需要以某种方式动态。

我通过这样的泛型类解决了我的问题:

public class MyGenericType<T>
{
    public T Data;
    public string SomeString;
}

然后实例化并使用该类,如下所示;

List<MyGenericType<dynamic>> myList = new List<MyGenericType<dynamic>>();
myList.Add(new MyGenericType<dynamic>(){ Data = "my string" });
myList.Add(new MyGenericType<dynamic>(){ Data = null });
myList.Add(new MyGenericType<dynamic>(){ Data = new EvenOtherType() });

警告: 您可以访问孩子的成员,但他们在列表中的所有成员都不一样!它会让你编译它,但它可能会在运行时中断!

例如,我的类“EvenOtherType”上有一个“DoSomething()”方法。如果我在循环遍历列表时尝试调用它,显然字符串和int版本将在运行时崩溃!

小心!

答案 4 :(得分:0)

你的最后一行“每个玩具都有不同的构造函数”意味着你需要一个玩具基础类,每个特定的玩具继承自该类:

public abstract class Toy{} // abstract base class

public class Ball : Toy
{
    public Ball(){} // Ball constructor
}

或定义通用玩具的接口,每个特定类型的玩具实现接口...接口或基类可以定义通用列表。

您需要这个的原因是,如果您对所有类型的Toy使用单个类,那么您将无法根据类型更改构造函数...这不是行为泛型支持。