-1

所以我有一个程序,它开始使用精确数量的步骤构建从给定起点到同一点的可能路径字典。

我首先为开始和结束步骤的每个独特组合添加一个字典行,然后我有一个方法,它基本上可以找到所有可以填充其间剩余步骤的独特组合。

它看起来像这样:

        private int CreatePathsWithAllSteps(int pathKey)
    {
        try
        {
            //Make sure we reiterate through the list of paths for all the configured steps to complete the paths.
            for (int i = 0; i < MaxSteps - 2; i++)
            {
                //Create a fresh list of new paths with each complete iteration so we're not duplicating added paths
                Dictionary<int, TravelPath> newPaths = new Dictionary<int, TravelPath>();

                foreach (KeyValuePair<int, TravelPath> path in Paths)
                {
                    //We need to find the list of Destination Pairs we can use with the existing path
                    Dictionary<string, DestinationPair> pairsToUse = FindDestinationPairsToUseNext(path, i+1);
                    bool isfirstiteration = true;

                    //***Issue*** 1 - Creating the copy of the KVP so I can reuse it to create all possible combinations of it with the next step in the path.
                    //I use the values of the KVP from the dictionary i'm iterating through. This seems to create them as reference paths back to the original dictionary.  
                    KeyValuePair<int, TravelPath> pathCopy = new KeyValuePair<int, TravelPath>(path.Key, path.Value);
                    
                    foreach (KeyValuePair<string, DestinationPair> TP in pairsToUse)
                    {
                        //Make sure we're using already existing paths first and then create new ones for all other options. 

                        if (isfirstiteration)
                        {
                            //First time through use path already created at this point
                            if (path.Value.Path[i].Name != TP.Value.Name)
                            {
                                //Destination Pair is different to the previous pair so add it
                                path.Value.Path[i + 1] = TP.Value;
                                isfirstiteration = false;
                            }
                            
                        }
                        else
                        {
                            //for this one, create a new copy of the path we're working on and then update with next step
                            if (pathCopy.Value.Path[i].Name != TP.Value.Name)
                            {
                                //***Issue*** 2 - When I use the value of the copied KVP to create a new KVP to add to the dictionary this also seems to create a reference back to the copy and by extension the original value from the original dictionary we're iterating through. 
                                KeyValuePair<int, TravelPath> tempTP = new KeyValuePair<int, TravelPath>(pathKey, pathCopy.Value);
                                //***Issue*** 3 - I then edit the value of the KVP created in the previous line, and this changes the value in the original main Paths dictionary.
                                tempTP.Value.Path[i + 1] = TP.Value;
                                if (!PathValueComparer(tempTP, newPaths, Paths))
                                {
                                    newPaths.Add(pathKey, pathCopy.Value);
                                    pathKey++;
                                }
                                
                            }
                            
                        }
                    }

                   
                }

                foreach(KeyValuePair<int, TravelPath> path in newPaths)
                {
                    Paths.Add(path.Key, path.Value);
                }
            }
            return pathKey;
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
            return pathKey;
        }

    }

我已经添加了一些带有***Issue***前缀的额外注释,这样您就可以看到我认为问题出在哪里以及我认为这里发生了什么。

现在距离我上次进行常规编码已经 3 或 4 年了,所以我有点生疏了(请不要过多地判断我的代码,哈哈),但我确实记得以前在处理字典时遇到过类似的问题,而且我似乎请记住,解决方案是拥有一个实现 IDictionary 和 ICloneable 的自定义字典,因此您可以调用.Clone()创建一个未引用的副本,然后可以对字典进行临时编辑并使用这些值,而不会将它们引用回您的源字典。

我想我会尝试用 KVP 做一些类似的事情,但似乎没有一个 KVP 接口可以实际用作自定义实现的基础。

我觉得我可能只是创建一个可克隆的字典,然后在从克隆版本中获取我需要的 KVP 副本之前克隆它,但是对于这项工作,我真的不需要整个字典都是可克隆的,只是KVP,所以看起来有点浪费

所以我想问题是,有没有更好的方法来打破 KVP 和 Dictionary 的值之间的引用?

4

1 回答 1

0

本质问题是,尽管内存中有两个不同的 KeyValuePair,但它们引用同一个 TravelPath 类

 //this line
KeyValuePair<int, TravelPath> pathCopy = new KeyValuePair<int, TravelPath>(path.Key, path.Value);

//sets up this in memory

123 <-- path.Key, path.Value --> TravelPathX <-- pathCopy.Value, pathCopy.Key --> 123

值类型被克隆,因此有两个不同的 123,但两个 Value 属性都引用 TravelPath 的同一个实例。修改 TravelPath 实例的属性意味着 path 和 pathcopy 都可以看到

没有看到 TravelPath 很难进一步了解,但简单的解决方案是创建一个新的 TravelPath 并复制您想要的属性。请记住克隆您想要修改的任何内容,例如,如果 TravelPath 有一个列表,它也需要newing(如果您要修改列表中的对象而不是删除和创建新对象,那么这些对象new也需要 ing ..)

//this line
var pathCopy = new KeyValuePair<int, TravelPath>(path.Key, new TravelPath { Name = path.Value.Name ... });

//Would set up this in memory
123 <-- path.Key, path.Value --> TravelPathX
                                 TravelPathY <-- pathCopy.Value, pathCopy.Key --> 123
于 2021-08-30T06:11:40.153 回答