C#Dictionary意外引用行为

时间:2015-03-02 10:36:40

标签: c# dictionary

背景

我理解C#字典是一个表示键值对的集合的数据结构。

以下代码(LINQPad C#Program)和后续输出屏幕截图演示了我如何尝试使用字典来(1)按创建顺序保留类(RefInteger)的最新两个实例,以及(2)允许"删除"最近的RefInteger实例。地点:

  • refDict[A]返回最新创建的RefInteger实例
  • refDict[B]返回第二个最近创建的实例,
  • refDict[C]只需要" resupply"如果最新的refDict[B]实例被{#1}}删除,则RefInteger" (通过RegressEntries方法)。

代码:

public enum Pos { A, B, C }
public Dictionary<Pos, RefInteger> refDict = new Dictionary<Pos, RefInteger>(); 

void Main()
{
    Create(1);
    refDict.Dump("Create 1");
    Create(2);
    refDict.Dump("Create 2");
    Create(3);
    refDict.Dump("Create 3");
    RegressEntries();
    refDict.Dump("Regress");
    Create(4);  
    refDict.Dump("Create 4");
    Create(5);  
    refDict.Dump("Create 5");
}

private void Create(int value)
{
    ProgressEntries();
    refDict[Pos.A] = new RefInteger(value); 
}

private void ProgressEntries()
{
    if (refDict.ContainsKey(Pos.B))
        refDict[Pos.C] = refDict[Pos.B];
    if (refDict.ContainsKey(Pos.A))
        refDict[Pos.B] = refDict[Pos.A];
}

private void RegressEntries()
{
    if (refDict.ContainsKey(Pos.B))
        refDict[Pos.A] = refDict[Pos.B];
    if (refDict.ContainsKey(Pos.C))
        refDict[Pos.B] = refDict[Pos.C];
}

public class RefInteger
{
  public int Value;

  public RefInteger(int value)
  {
      Value = value;
  }
}

截图

LINQPad Output

问题

虽然上面简化的示例的行为与我期望字典的行为一样,但我有字典,其中ProgressEntries方法似乎导致每个后续的refDict键指向最近的值。这相当于从简化示例中接收以下输出:

enter image description here

请建议如何做到这一点。

注意,我已确认&#34;进展&#34; ProgressEntries方法中的内容是正确的(即将refDict[B]移至refDict[C]然后将refDict[A]移至refDict[B])。
问题字典虽然很小,但使用复合键来返回值。搬到另一个结构并不是一个真正的选择 示例代码使用简单类(refInteger)而不是基本类型,以确保我对字典行为的理解是正确的。

由于

香农

修改 请在下面找到所请求的示例控制台应用代码。该示例的行为与预期一致。我将继续深入研究实际代码的问题。

using System;
using System.Collections.Generic;

namespace DictionaryProgress
{
    class Program
    {
        public enum Pos { A, B, C }

        static void Main()
        {
            var refDictionary = new RefDictionary();
            refDictionary.Update();
            Console.ReadKey();
        }

        public class RefDictionary
        {
            public Dictionary<Pos, RefInteger> RefDict = new Dictionary<Pos, RefInteger>();

            public void Update()
            {
                Create(1);
                Console.WriteLine("Create 1 : {0}", ToString());
                Create(2);
                Console.WriteLine("Create 2 : {0}", ToString());
                Create(3);
                Console.WriteLine("Create 3 : {0}", ToString());

                RegressEntries();
                Console.WriteLine("Regress  : {0}", ToString());
                Create(4);
                Console.WriteLine("Create 4 : {0}", ToString());
                Create(5);
                Console.WriteLine("Create 5 : {0}", ToString());
            }

            private void Create(int value)
            {
                ProgressEntries();
                RefDict[Pos.A] = new RefInteger(value);
            }

            private void ProgressEntries()
            {
                if (RefDict.ContainsKey(Pos.B))
                    RefDict[Pos.C] = RefDict[Pos.B];
                if (RefDict.ContainsKey(Pos.A))
                    RefDict[Pos.B] = RefDict[Pos.A];
            }

            private void RegressEntries()
            {
                if (RefDict.ContainsKey(Pos.B))
                    RefDict[Pos.A] = RefDict[Pos.B];
                if (RefDict.ContainsKey(Pos.C))
                    RefDict[Pos.B] = RefDict[Pos.C];
            }

            public override string ToString()
            {
                var PosA = RefDict.ContainsKey(Pos.A) ? 
                    RefDict[Pos.A].Value : 0;
                var PosB = RefDict.ContainsKey(Pos.B) ?
                    RefDict[Pos.B].Value : 0;
                var PosC = RefDict.ContainsKey(Pos.C) ? 
                    RefDict[Pos.C].Value : 0;

                return string.Format("{0}, {1}, {2}", PosA, PosB, PosC);
            }
        }

        public class RefInteger
        {
            public int Value;

            public RefInteger(int value)
            {
                Value = value;
            }
        }            

    }
}

1 个答案:

答案 0 :(得分:1)

正如评论者Jon Skeet建议的那样,如果您需要有关无效代码的帮助,那么您在问题中发布的代码应该是该代码。向我们展示 工作的代码,然后要求我们解释为什么某些其他代码不起作用并不是一个很好的方法来获得答案。

也就是说,假设代码不起作用甚至远远像代码那样有用,我会说对你所看到的行为最明显的解释是代码的破坏版本没有创建一个值对象的新实例,而只是重用以前创建的实例。

也就是说,所描述的行为显示了典型的“引用型复制”症状。

请注意,在 工作的代码中,您将值对象添加到字典时创建一个全新的实例:

RefDict[Pos.A] = new RefInteger(value);

我敢打赌,在的代码中,不是将new ...something...指定为值,而是指定对其他对象的引用。

当然,如果没有a good, minimal, complete code example显示的代码版本,就不可能确定它有什么问题。因此,如果上述内容无法解释为什么无效的代码行为不正确,则应编辑问题以包含正确的代码示例。