【问题标题】:C# Generic List of Generic List of Multiple TypesC# 多类型泛型列表的泛型列表
【发布时间】:2010-01-16 01:36:43
【问题描述】:

这是我的问题的抽象和简化:

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

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

我希望能够创建具有许多不同类型的玩具,然后将它们插入到具有另一种指定类型的 Box 中。然后我希望能够将盒子添加在一起,返回一个具有最大类型的盒子。

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

任何人都可以提供任何帮助,我们将不胜感激。

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

未来可能的澄清:

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

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

更新:

我将 Box to 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

Final:我最终取消了 Box 是通用的要求。然后我使用反射来创建动态类型的玩具。谢谢大家。

【问题讨论】:

  • 公开名单 = new List();公共列表 = 新列表();这些不会编译,变量名被遗漏了。 “玩具也必须有一个列表作为成员。” :这个列表中有什么,它的目的是什么?建议您重新编写问题描述以更清楚地表达您的目标和意图:也许区分一个拥有一个和只有一个玩具的“ToyBox”和一个拥有一个或多个“ToyBox 对象的 BoxOfToys”?
  • 我同意 BillW 关于列表问题的观点,并要求进一步澄清 Toy 实际添加到泛型类型中的内容。 “Ball”和“Horse”对我来说已经像是玩具了,那么“Toy”或“Toy”是什么? Horse 和 Ball 有共同的基类吗?

标签: c# generics


【解决方案1】:

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

建模“B 中的 A”的方法是使用泛型。一组可以容纳一种东西的盒子将被建模为Box&lt;T&gt;。只能装玩具的盒子是Box&lt;Toy&gt;。一组可以容纳一种东西的盒子,而那个东西必须是玩具,那就是Box&lt;T&gt; where T : Toy

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

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

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

你说

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

为什么? 玩具没有物品清单。所以不要这样建模。相反,一个盒子在逻辑上被建模为盒子内的玩具列表。 (或者,由于一个盒子通常不应用排序,并且一个盒子只包含独特的玩具,也许一套玩具会更好。)

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

好的。所以对Box&lt;T&gt; 的操作是void Insert(T item)。你可以把一个玩具装进一盒玩具,你可以把一个洋娃娃装进一盒洋娃娃,但你不能把一个球装进一盒洋娃娃。

然后我希望能够将盒子添加在一起,返回一个具有最大类型的盒子。

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

这是我将如何建模。我们已经有了玩具层次结构。我会继续说,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&lt;T&gt; 在 C# 4 中是协变的。在 C# 3 中,这会更棘手;您必须执行以下操作:

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

这有意义吗?

【讨论】:

  • 这很有道理。合并部分非常好。但是,每个玩具都必须有一个类型 T 的列表。认为类型 T 是本例中的材料。我可能有一个塑料玩具,其中包含构建所需的塑料零件列表/数组。 [参见原始帖子中的更新 2]
  • 埃里克是绝对正确的;您坚持 must 成为规范的一部分的事情正是作为规范的一部分没有意义的事情。如果Toy 必须“包含”一个部件列表(已经很可疑,因为这应该是一种引用关系,而不是包含),那么将一个List&lt;Material&gt; 放在抽象的Toy 基类中,或者更好的是,有一些东西就像一个抽象的GetRequiredMaterials() 方法。没有理由让它成为泛型类型参数 - 如果它似乎是必需的,那么它表明模型存在问题。
  • 他一开始就说这个例子是对他的问题的抽象和简化。所以很有可能他的真正问题不涉及玩具和盒子,而是完全不同的东西。这可能只是一个(不是很好选择的)示例。
【解决方案2】:

我不确定这是否是您真正想要做的,因为它有点骇人听闻。如果您想要所有可能的玩具类型的列表,包括 Box 可能不知道的那些,您将需要使用您的通用参数来过滤出正确的玩具。我不建议这样做,除非 Box 只是用于将常见事物塞入其中的白板(例如 BlackBoard)。

例如,您可以只有一个 List 并且所有方法在操作它们之前检查内容的类型是否正确。您还可以使用 Dictionary 来跳过逐项过滤步骤,但会导致一些继承问题(动物列表不包括添加到长颈鹿列表中的内容)。

这是 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
{
    ...
}

【讨论】:

  • 这是一个有趣的想法。是的,这有点 hacky :)
【解决方案3】:

我引用 MSDN:

泛型类封装操作 不是特定于特定的 数据类型。最常见的用途 泛型类与集合 像链表,哈希表, 堆栈,队列,树等在哪里 添加和删​​除等操作 集合中的项目是 以几乎相同的方式执行 无论数据类型如何 存储。

根据您的描述,最接近的适用对象是 Box,但话又说回来,您知道它将包含 Toys,并且有一个最大 ToySize,并且 Toys 有一个类型(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; }
    }

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

【讨论】:

    【解决方案4】:

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

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

    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 版本会在运行时崩溃!

    小心!

    【讨论】:

      【解决方案5】:

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

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

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

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

      【讨论】:

      • 我应该更清楚。我的意思是像 Toy 和 Toy 一样的类型构造函数。如果可能的话,我真的很想避免多个类都从 Toy 继承。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2010-12-06
      • 1970-01-01
      • 1970-01-01
      • 2015-04-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多