尝试创建一个列表的二维数组

8

我正在尝试编写一个包含数字生物体的模型。在模型中,我希望环境是一个固定的二维数组,但每个单元格都需要包含其中的生物列表。我尝试使用不规则数组,但由于占用元素的数量在程序运行过程中变化很大,所以需要使用比数组更灵活的东西。我已经尝试过制作类型为列表的二维数组,但出现了错误。

    List<Creature>[,] theWorld;


    public Environment()
    { 
        List<Creature>[,] theWorld = new List<Creature>[100,100];
    }

    public void addCreature(Creature c)
    {
       for (int x = 0; x < 100; x++)
        {
            for (int y = 0; y < 100; y++)
            {
                theWorld[x, y].Add (c);

            } } }

这是我试图在开始时声明数组类型(包含有机体列表)并在之后尝试将生物(c)添加到数组的每个元素中的列表的一段代码。
当我运行代码时,出现以下错误信息: "System.NullReferenceException" 类型的未处理异常发生在 HGT_sim_2.exe 中 。附加信息:对象引用未设置为对象的实例。" 并且高亮显示了 "World[x,y].Add(c);" 这一行。
如果有人能告诉我我的问题在哪里,甚至更好的话,提供一个解决问题的方法,那就太棒了。谢谢!

通常在映射二维数组时,y先于x,但我想顺序是主观的。从左上角开始按y然后x进行操作将提供准确的视觉输出。 - Ash Blue
6个回答

8

你的数组最初只含有很多空值。你需要实际上创建列表...

for(int x = 0 ; x < 100 ; x++)
    for(int y = 0 ; y < 100 ; y++)
        theWorld[x,y] = new List<Creature>();

个人而言,我认为这将是一种昂贵的做事方式...

这在某种程度上取决于数据是否“稀疏” - 即大多数单元格通常都被占用吗?例如,一个简单(但可能更有效)的方法是使用类似于multi-map的东西;即。

Point pt = new Point(x,y);
theWorld.Add(pt, someCreature);

其中theWorld可能是类似于EditableLookup<Point, Creature>(使用来自“MiscUtil”的EditableLookup<,>)。这样,您仍然可以按坐标查询它,并且可以在一个坐标上拥有多个生物,但您不必为每个单元分配空间。并且因为它作为字典运行,所以速度很快。虽然不如平坦数组快,但它将扩展到更大的(稀疏)网格……当然,如果网格上每个单元都有生物,它可能会更昂贵!因此需要了解您的数据。


PowerCollections 包含一个 MultiDictionary<K,T>,它将会有所帮助。 - ShuggyCoUk
确实。如果你在某个时候没有编写过自己的多重映射,那么你就没有尝试过 ;-p - Marc Gravell

3
你需要初始化数组的每个元素,例如:
for (int x = 0; x < 100; x++)
    {
        for (int y = 0; y < 100; y++)
        {
            theWorld[x, y] = new List<Creature>();

        } }

1
在你发布添加到列表的代码之前,请先执行那个操作;-) - Eric J.
刚刚发现这个用法!:) 谢谢! - adeelx

2

Here's the fix:

List<Creature>[,] theWorld;


public Environment()
{ 
    theWorld = new List<Creature>[100,100]; // Remove the type, you were creating a new list and throwing it away...

    for(int x = 0 ; x < 100 ; x++)
       for(int y = 0 ; y < 100 ; y++)
          theWorld[x,y] = new List<Creature>();    
}

public void addCreature(Creature c)
{
   for (int x = 0; x < 100; x++)
    {
        for (int y = 0; y < 100; y++)
        {
            theWorld[x, y].Add (c);

        } } }

在构造函数中添加了一条注释,以便告知您。 - ConsultUtah

0
你已经创建了数组对象来保存你的列表,但是你还没有创建列表本身。在你的构造函数中,你需要执行以下操作:
for (int x = 0; x < 100; x++) 
    for (int y = 0; y < 100; y++)
        theWorld[x,y] = new List<Creature>();

另一个问题:您在构造函数中还将theWorld定义为局部变量,这意味着您在Environment上的theWorld字段也未初始化。
然而,10,000个List可能过于繁琐,不符合您实际需要的要求。如果您的环境确实需要每个点都有一个Creature,并且一些生物可能会移动到其他点(在一个点上有多个生物),那么使用Dictionary<Point,IList<Creature>>作为您的模型可能比使用10,000个列表更有意义。
public void Add(Creature c, Point at)
{
    IList<Creature> list;
    if (!theWorld.TryGetValue(at)) {
        list = theWorld[at] = new List<Creature>();
    }
    list.Add(c);
}

您可以类似地实现MoveRemove方法。此外,请注意,您正在向每个点添加相同的生物,这可能意味着在您的环境中所有点上都有一个生物。如果您实际上是在模拟这种情况,那么您可能需要为每个点创建一个new Creature()

部分正确,但如果不修复构造函数中的代码,它将无法正常工作。 - ConsultUtah
编辑以包括那个建议和另一个建议。 - Jason

0

当你这样做时:

List<Creature>[,] theWorld = new List<Creature>[100,100];

你正在创建一个 List<Creature> 引用的数组,但它们都是空的(指向 null,而不是有效的 List)。你需要初始化每个单独的元素:
for (int x = 0; x < 100; x++) {
    for (int y = 0; y < 100; y++) {
       theWorld[i,j] = new List<Creature>();
    }
}

一旦你完成了这个操作,就可以在各个成员上调用 .Add 方法。

0

你做得差不多对了。你的变量是一个二维数组,其中包含List<Creature>。现在,List<Creature>是一种引用类型,所以数组在初始化时会将所有成员都设为null。因此你会得到NullReferenceException异常。问题出在这一行:

theWorld[x, y].Add (c);

基本上等同于

null.Add (c);

你需要做的就是初始化所有成员,使其包含 List<Creature> 的实例。最好的方法是在构造函数中完成。只需像这样重写:

public Environment()
{ 
    theWorld = new List<Creature>[100,100];
    for(int x = 0 ; x < 100 ; x++)
        for(int y = 0 ; y < 100 ; y++)
            theWorld[x,y] = new List<Creature>();
}

现在所有操作都将按预期工作。

另外请注意,在您的示例中,您正在创建与类成员同名的局部变量。这样,您根本不初始化类成员-它保持为null。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接