首页 > 解决方案 > 嵌套的 foreach() 迭代混合了目标集合

问题描述

我正在尝试在太空工程师中编写脚本,并且我有一个 class 的集合_Filter,它位于另一个 class_Inventory中。这是我要执行的代码:

public void InventorySetup(_Inventory inventory)
{

    if (inventory.InvBlock != null)                                                                             // Check if block exists
    {
        string[] data = inventory.InvBlock.CustomData.Split('\n');                                              // Break customData into lines

        foreach (string nextline in data)                                                                       // Iterate each line
        {
            if (nextline.Length > 0)                                                                            // Line must contain information
            {
                string[] lineblocks = nextline.Split(' ');                                                      // Break each line into blocks

                if (lineblocks.Length > 1)                                                                      // There must be more than one block to have filter candidate and desired update
                {
                    string[] itemID = new string[2];

                    if (lineblocks[0].Contains(":"))
                        itemID = lineblocks[0].Split(':');                                                      // split the type from subType


                    else if (lineblocks[0].Contains("!"))
                        itemID = new string[] { "", "" };

                    else
                        itemID = new string[] { "null", "null" };

                    foreach (_Filter nextFilter in inventory.Filters)
                    {
                        if (ContainsCIS(nextFilter.ItemType, itemID[0]) && ContainsCIS(nextFilter.ItemSubType, itemID[1]))
                        {
                            for (int i = 1; i < lineblocks.Length; i++)
                            {
                                switch (lineblocks[i][0])
                                {
                                    case '#':
                                        nextFilter.Target = (MyFixedPoint)float.Parse(lineblocks[i].Replace('#', ' '));

                                        break;

                                    case '+':
                                        if (ContainsCIS(lineblocks[i], "in"))
                                            nextFilter.IN_BOUND = true;
                                        if (ContainsCIS(lineblocks[i], "out"))
                                            nextFilter.OUT_BOUND = true;
                                        break;

                                    case '-':
                                        if (ContainsCIS(lineblocks[i], "in"))
                                            //                                                    nextFilter.IN_BOUND = false;

                                            if (ContainsCIS(lineblocks[i], "out"))
                                                nextFilter.OUT_BOUND = false;
                                        break;
                                }
                            }
                        }
                    }
                }

                if (nextline[0] == '&')
                {
                    if (ContainsCIS(nextline, "empty"))
                    {
                        if (nextline.Contains("-"))
                            inventory.EMPTY = false;
                        else
                            inventory.EMPTY = true;
                    }
                    if (ContainsCIS(nextline, "fill"))
                    {
                        if (nextline.Contains("-"))
                            inventory.FILL = false;
                        else
                            inventory.FILL = true;
                    }
                    if (ContainsCIS(nextline, "active"))
                    {
                        if (nextline.Contains("-"))
                            inventory.ACTIVE = false;
                        else
                            inventory.ACTIVE = true;
                    }
                    if (ContainsCIS(nextline, "clean"))
                    {
                        if (nextline.Contains("-"))
                            inventory.CLEAN = false;
                        else
                            inventory.CLEAN = true;
                    }

                    if (ContainsCIS(nextline, "reset"))
                        inventory.Reset();

                }
            }
        }
    }
}

所以基本上发生了什么,_Inventory类中有一个成员直接引用我正在尝试配置的库存块。这个成员有它自己的成员叫做CustomData,它是一个可以在游戏中编辑的字符串对象。所以我把它分成每行的一个数组,然后根据每一行的上下文性质处理每一行。

首先,通过再次用空格字符分隔每一行,将行分解为“块”,然后进一步分析。如果块数大于1,这意味着存在预期的“目标过滤器”和某种后续限定符。更改其白名单/黑名单设置,或更改目标最大值(0 表示无限制,是此成员的默认值)。

现在_Inventory该类已经包含一个预先存在的_Filter类集合,并且仅通过迭代来更新它的成员。例如,如果我想将“IN_BOUND”矿石列入黑名单,我会添加“ore: -in”行。或者如果我想将出界钢板列入黑名单,请使用“:steelplate -out”。(冒号前的名称表示类别,冒号后的名称表示项目的具体类型。感叹号表示集合中的所有项目)。该ContainCIS()方法是我简单地搜索包含的字符串“区分大小写”的方法。除了更改过滤器之外,_Inventory该类还拥有一些用于控制所需功能的 bool 成员。他们自称EMPTY,,,, FILL_ACTIVECLEAN(干净的布尔与有时会塞满错误材料的生产块有关,与我的问题无关)

我的实际问题:
调用此代码时,如果根集合中只有一个_Inventory,则一切正常。但是,如果我有多个,则布尔值会正常更新,但过滤器会以相反的顺序更新。

所以说例如我有_Inventory A, 和_Inventory B。如果我将该行添加到“A”引用的块的自定义数据中,则正确的布尔值会更改。但是,如果我更改“A”上的过滤器设置,它会更新为“B”。

编辑:这是调用 InventorySetup() 的类和方法:

    public class _Inventory
    {
        public IMyTerminalBlock InvBlock;
        public _BlockType BlockType;        // Cargo, Assembler, Refinery
        public _Filter[] Filters;           // Expected inventory candidates (Refer to _Filter libraries)
        public bool FILL;                   // Attempt fill action on all IN_BOUND "true" candidates
        public bool EMPTY;                  // Attempt empty action on all OUT_BOUND "true" candidates
        public bool ACTIVE;                 // Actively being manipulated by program ("Use conveyor system" property will be disabled on prod. blocks)
        public bool CLEAN;                  // Actively clear overburdened assembler inputs, or un-scheduled refinery inputs (not used for cargos)

        public _Inventory(IMyTerminalBlock invBlock, _BlockType blockType, _Filter[] filters, bool active = true)
        {
            InvBlock = invBlock;
            BlockType = blockType;
            Filters = filters;
            FILL = false;
            EMPTY = false;
            ACTIVE = active;
            CLEAN = true;
        }

        public void Reset()
        {
            foreach (_Filter nextFilter in Filters)
            {
                nextFilter.Target = 0;
                nextFilter.IN_BOUND = true;
                nextFilter.OUT_BOUND = true;
            }
        }
    }

public class _Filter
    {
        public string ItemType;
        public string ItemSubType;

        public MyFixedPoint Target;
        public int Priority;

        public bool IN_BOUND;
        public bool OUT_BOUND;

        public _Filter(string itemType = "null")
        {
            ItemType = itemType.Split(':')[0];
            ItemSubType = itemType.Split(':')[1];
            Target = 0;                     // 0 means no target value, any amount aloud
            Priority = 0;                   // Priority for in-bound requests (WIP)
            IN_BOUND = true;                // Default to whitelist items in & out of inventory
            OUT_BOUND = true;
        }
    }

public void InventoryListUpdate()
    {
        Inventories.RemoveAll(x => x.InvBlock == null);
        Inventories.RemoveAll(x => !x.InvBlock.CustomName.Contains(Signature));

        foreach (IMyCargoContainer nextCargo in Cargos)
        {
            if (Inventories.FindIndex(x => x.InvBlock == nextCargo) < 0 && nextCargo.CustomName.Contains(Signature))
                Inventories.Add(new _Inventory((IMyTerminalBlock)nextCargo, _BlockType.CARGO, FullLibrary));
        }
        foreach (IMyRefinery nextRefinery in Refineries)
        {
            if (Inventories.FindIndex(x => x.InvBlock == nextRefinery) < 0 && nextRefinery.CustomName.Contains(Signature))
                Inventories.Add(new _Inventory((IMyTerminalBlock)nextRefinery, _BlockType.ASSEMBLER, RefineryLibrary));
        }
        foreach (IMyAssembler nextAssembler in Assemblers)
        {
            if (Inventories.FindIndex(x => x.InvBlock == nextAssembler) < 0 && nextAssembler.CustomName.Contains(Signature))
                Inventories.Add(new _Inventory((IMyTerminalBlock)nextAssembler, _BlockType.ASSEMBLER, AssembleLibrary, false));
        }

        foreach (_Inventory nextInventory in Inventories)
        {
            if (bInventoryRunning && nextInventory.ACTIVE)
                InventoryUpdate(nextInventory);
            else
                InventorySetup(nextInventory);
        }
    }

标签: c#foreachnestediteration

解决方案


因此,当我今天在太空工程师中启动代码时,它突然开始按预期工作......我完全不知道如何或为什么。我倾向于与游戏如何处理其游戏内脚本相关的内存泄漏问题,但是清理代码以某种方式“按摩”它以按预期执行的可能性很小。正如我之前所说,它的行为在某种程度上符合预期,只是随着更高集合规模的增长,它出现了意想不到的行为。由于每个 _Inventory 都是单独处理的,因此它的成员应该没有办法转移到另一个上,除非在后台发生了一些奇怪的 GC-voodoo。让我的脚本正常运行虽然令人欣慰,但知道我可能永远也不会知道为什么它一开始就搞砸了,这是一种更糟糕的感觉,因为我没有真正的坚实基础来避免将来出现这个问题。谢谢大家的意见。


推荐阅读