C# 多类型泛型列表的泛型列表

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2075866/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-06 23:18:02  来源:igfitidea点击:

C# Generic List of Generic List of Multiple Types

c#generics

提问by Chris

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:

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

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:

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

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.

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

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.

解决方案可以在 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.

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

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.

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

Update:

更新:

I fixed the Box to Box that was a typo.

我修复了 Box 到 Box,这是一个错字。

Update 2:

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

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

采纳答案by Eric Lippert

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.

对“A of B”建模的方法是使用泛型。一组可以容纳一种东西的盒子将被建模为Box<T>. 一个只能装玩具的盒子Box<Toy>。一套可以装一种东西的盒子,那个东西必须是玩具才会是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.

到现在为止还挺好。但是 的概念Toy<T>并没有映射到现实生活中的任何东西。你可能有一盒饼干或一盒玩具,但你没有饼干玩具、洋娃娃或长颈鹿玩具。“toy of”这个概念没有任何意义,所以不要模仿它

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.

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

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 setof 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.

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

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.

好的。所以对 的操作Box<T>void Insert(T item)。你可以把一个玩具放进一盒玩具,你可以把一个洋娃娃放进一盒洋娃娃,但你不能把一个球放进一盒洋娃娃。

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.

这是我将如何建模。我们已经有了玩具层次结构。我会继续说,一盒 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(); }

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

到目前为止一切都很无聊。现在我们来到有趣的地方。这仅适用于 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:

这最后一点之所以有效,IEnumerable<T>是因为它在 C# 4 中是协变的。在 C# 3 中,这会更棘手;你必须做这样的事情:

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

Does that make sense?

那有意义吗?

回答by STW

Your very last line "every toy has a different constructor" implies that you need either a Toy base-class, with each specific toy inheriting from that class:

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

public abstract class Toy{} // abstract base class

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

or an interface to define the generic Toy, with each specific type of Toy implementing the interface... either the interface or base class can define the generic list.

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

The reason you need this is that if you use a singleclass for all types of Toythen you won't be able to alter the constructor based on the type... that's just not a behavior generics support.

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

回答by Craig Gidney

I'm not sure if this is what you actually want to do, since it is a bit hackish. If you want lists of every conceivable toy type, including ones Box might not know about, you're going to need to use your generic parameters to filter out the correct toys. I don't recommend doing this unless the Box is just a blank slate for stuffing common things into (eg. a BlackBoard).

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

For example, you could have just one List<Object> and all the methods check if the contents are of the correct type before manipulating them. You could also use a Dictionary<Type, Object> in order to skip the term-by-term filtering step, but cause some problems with inheritance (the list of animals wouldn't include things added to the list of giraffes).

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

Here is a simple implementation of the Dictionary approach:

这是 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)];
    }
}

You can adapt this code to restrict the lists to be of a specific type, and you can adapt it further so you can join lists which share a common base type. That might look like this:

您可以修改此代码以将列表限制为特定类型,并且您可以进一步修改它以便您可以连接共享公共基类型的列表。这可能看起来像这样:

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

回答by Dynami Le Savard

I quote MSDN:

我引用 MSDN:

Generic classes encapsulate operations that are not specific to a particular data type. The most common use for generic classes is with collections like linked lists, hash tables, stacks, queues, trees and so on where operations such as adding and removing items from the collection are performed in much the same way regardless of the type of data being stored.

通用类封装了不特定于特定数据类型的操作。泛型类最常见的用途是与链表、哈希表、堆栈、队列、树等集合一起使用,其中从集合中添加和删除项目等操作以几乎相同的方式执行,而不管数据类型如何存储。

Given your description, the closest object to which that would be applicable would be Box, but then again, you know its going to contain Toys, and has a maximum ToySize, and that Toys have a type (Ball, Horse...). I might be wrong here, but going with generics might not really be necessary. I'm thinking something like :

根据您的描述,最接近的对象是 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; }
    }

Unless you have a particular reason behind using generics.

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

回答by Dan Rayson

I had this problem when constructing a list of generic types that I didn't know the generic part of at compile time, they needed to be dynamic somehow.

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

I solved my problem by having the generic class like this:

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

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

And then instantiating and using that class as follows;

然后按如下方式实例化和使用该类;

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() });

WARNING: You can access the members of the children, but they're not the same on all in the list! It'll let you compile it, but it might break during runtime!

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

For example, I have a "DoSomething()" method on my class "EvenOtherType". If I try to call that while looping over the list, obviously the string and the int versions will crash during runtime!

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

Be careful!

当心!