Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/21.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 使用PhysicalAddress作为键时,字典中存在重复键_C#_.net_Dictionary_Duplicates - Fatal编程技术网

C# 使用PhysicalAddress作为键时,字典中存在重复键

C# 使用PhysicalAddress作为键时,字典中存在重复键,c#,.net,dictionary,duplicates,C#,.net,Dictionary,Duplicates,因此,我遇到了一个有趣的问题,当使用PhysicalAddress类型的键时,我在C#Dictionary中得到了重复的键。这很有趣,因为它只发生在很长一段时间之后,我无法在完全不同的机器上使用相同的代码在单元测试中重现它。我可以在WindowsXPSP3机器上可靠地复制它,但必须让它一次运行几天,即使这样,它也只发生一次 下面是我正在使用的代码,下面是该部分代码的日志输出 代码: GetFromTagReports代码 获取重复密钥的日志输出: 2013-04-26 18:28:35,608

因此,我遇到了一个有趣的问题,当使用PhysicalAddress类型的键时,我在C#Dictionary中得到了重复的键。这很有趣,因为它只发生在很长一段时间之后,我无法在完全不同的机器上使用相同的代码在单元测试中重现它。我可以在WindowsXPSP3机器上可靠地复制它,但必须让它一次运行几天,即使这样,它也只发生一次

下面是我正在使用的代码,下面是该部分代码的日志输出

代码:

GetFromTagReports代码

获取重复密钥的日志输出:

2013-04-26 18:28:35,608 [8] DEBUG ClassName - Detected tag (000CCC756081) with Exciter Id (0)
2013-04-26 18:28:35,608 [8] DEBUG ClassName - Detected tag is displayable (Unknown: ?56081)
2013-04-26 18:28:35,608 [8] TRACE ClassName - Keys
2013-04-26 18:28:35,608 [8] TRACE ClassName - Address: 000CCC755898
2013-04-26 18:28:35,608 [8] TRACE ClassName - Address: 000CCC756081
2013-04-26 18:28:35,618 [8] TRACE ClassName - Address: 000CCC755A27
2013-04-26 18:28:35,618 [8] TRACE ClassName - Address: 000CCC755B47
2013-04-26 18:28:35,618 [8] TRACE ClassName - Address: 000CCC756081
2013-04-26 18:28:35,618 [8] TRACE ClassName - Values
2013-04-26 18:28:35,618 [8] TRACE ClassName - Address: 000CCC755898 Name: Scotty McTester
2013-04-26 18:28:35,618 [8] TRACE ClassName - Address: 000CCC756081 Name: ?56081
2013-04-26 18:28:35,648 [8] TRACE ClassName - Address: 000CCC755A27 Name: JDTest1
2013-04-26 18:28:35,648 [8] TRACE ClassName - Address: 000CCC755B47 Name: 33 1
2013-04-26 18:28:35,648 [8] TRACE ClassName - Address: 000CCC756081 Name: ?56081
2013-04-26 18:28:35,648 [8] TRACE ClassName - Current tags: Scotty McTester, ?56081, JDTest1, 33 1, ?56081
请注意,所有事情都发生在单个线程上(请参见[8]),因此字典不可能同时被修改。这些摘录来自同一日志和同一流程实例。还请注意,在第二组日志中,我们得到了两个相同的键

我正在研究的内容:我已将PhysicalAddress更改为字符串,以查看是否可以将其从嫌疑犯列表中删除

我的问题是:

  • 在上面的代码中是否有我没有看到的问题
  • 物理地址上的相等方法有问题吗?(只是偶尔会出错?)
  • 这本词典有问题吗

Dictionary期望不可变对象作为键,并具有稳定的GetHashCode/Equals实现。 这意味着在将对象放入字典后,GetHashCode返回的值应该 不更改,对此对象所做的任何更改都不应影响Equals方法

虽然PhysicalAddress类被设计为不可变的,但它仍然包含一些扩展点, 它的不变性有缺陷

首先,它可以通过输入字节数组进行更改, 它不是复制的,而是通过引用传递的,如下所示:

var data = new byte[] { 1,2,3 };
var mac = new PhysicalAddress(data);
data[0] = 0;
其次,PhysicalAddress不是一个密封类,可以通过派生类进行更改 通过重写构造函数/GetHashCode/Equals方法实现。 但是这个用例看起来更像一个黑客,所以我们将忽略它,以及通过反射进行的修改

您的情况只能通过首先将PhysicalAddress对象放入dictionary来实现, 然后修改其源字节数组,然后将其包装到新的PhysicalAddress实例中

幸运的是,PhysicalAddress的GetHashCode实现只计算一次哈希, 如果修改了同一个实例,它仍然放在同一个字典桶中, 并再次以相等的方式定位

但是,如果源字节数组被传递到PhysicalAddress的另一个实例,其中 尚未计算-为新字节[]值重新计算哈希,找到新的存储桶, 并将副本插入字典。在极少数情况下,可以找到相同的铲斗 从新的散列,再次,没有重复插入

下面是重现问题的代码:

using System;
using System.Collections.Generic;
using System.Net.NetworkInformation;

class App
{
  static void Main()
  {
    var data = new byte[] { 1,2,3,4 };
    var mac1 = new PhysicalAddress(data);
    var mac2 = new PhysicalAddress(data);
    var dictionary = new Dictionary<PhysicalAddress,string>();
    dictionary[mac1] = "A";
    Console.WriteLine("Has mac1:" + dictionary.ContainsKey(mac1));
    //Console.WriteLine("Has mac2:" + dictionary.ContainsKey(mac2));
    data[0] = 0;
    Console.WriteLine("After modification");
    Console.WriteLine("Has mac1:" + dictionary.ContainsKey(mac1));
    Console.WriteLine("Has mac2:" + dictionary.ContainsKey(mac2));

    dictionary[mac2] = "B";
    foreach (var kvp in dictionary)
      Console.WriteLine(kvp.Key + "=" + kvp.Value);
  }
}
使用系统;
使用System.Collections.Generic;
使用System.Net.NetworkInformation;
类应用程序
{
静态void Main()
{
var data=新字节[]{1,2,3,4};
var mac1=新物理地址(数据);
var mac2=新物理地址(数据);
var dictionary=newdictionary();
字典[mac1]=“A”;
Console.WriteLine(“Has mac1:+dictionary.ContainsKey(mac1));
//Console.WriteLine(“Has mac2:+dictionary.ContainsKey(mac2));
数据[0]=0;
Console.WriteLine(“修改后”);
Console.WriteLine(“Has mac1:+dictionary.ContainsKey(mac1));
Console.WriteLine(“Has mac2:+dictionary.ContainsKey(mac2));
字典[mac2]=“B”;
foreach(字典中的var kvp)
控制台写入线(kvp.Key+“=”+kvp.Value);
}
}
注意注释行-如果我们取消注释它,“ContainsKey”方法将为mac2预计算哈希,即使在修改之后也是一样的

所以我的建议是找到一段生成PhysicalAddress实例的代码,并创建
为每个构造函数调用创建新的字节数组副本。

您可以注意到非工作运行不会同时发生。这可能是线程问题的一个参数。如何确保
displayableTags
不是共享对象?这是局部变量吗?财产?此外,请使用
TryGetValue
而不是
ContainsKey
。我可以确定,因为“displayableTags”是在线程构造函数调用的方法中本地创建的变量。我尝试了TryGetValue,它也做了同样的事情(我将把它添加到问题中)。此外,来自TryGetValue上的msdn文档:此方法结合了ContainesKey方法的功能和Item属性。您可以在一个块中发布代码吗?问题可能是你的日志函数也有问题,我们可以看到吗?我本来不想添加太多代码,以防它弄乱文章。现在发布了更多代码。对PhysicalAddress上的Equals方法进行反编译表明,它只是比较底层字节数组——它检查长度,然后逐个元素遍历数组元素。您是使用接受字节[]的构造函数创建标记地址,还是使用
PhysicalAddress.Parse(string)
?感谢您精心构造的答案:)不幸的是,我们已经在为每个构造函数调用创建新的字节数组。请参阅最近添加到问题中的代码。TagReport上的MacAddress属性在此之后再也不会分配给,只会被使用。另外,在那里创建的实例最终会进入GetTagReports调用?它在什么地方重复使用吗?字典的平均大小是多少?它多长时间被修改一次?没有更多的想法-尝试测试服务器的内存,在那个里你们可以重现这个问题。将所有字典访问方法放入lock(),以确保没有多线程问题。变量“physicalAddressBytes”在之后的任何位置都不会使用
var physicalAddressBytes = new byte[6];
ByteWriter.WriteBytesToBuffer(physicalAddressBytes, 0, protocolDataUnit.Payload, 4, 6);

var args = new TagReport
{
    Version = protocolDataUnit.Version,
    MacAddress = new PhysicalAddress(physicalAddressBytes),
    BatteryStatus = protocolDataUnit.Payload[10],
    ReceivedSignalStrength = IPAddress.NetworkToHostOrder(BitConverter.ToInt16(protocolDataUnit.Payload, 12)),
    ExciterId = IPAddress.NetworkToHostOrder(BitConverter.ToInt16(protocolDataUnit.Payload, 14))
};

public static void WriteBytesToBuffer(byte[] oldValues, int oldValuesStartindex, byte[] newValues, int newValuesStartindex, int max)
{
    var loopmax = (max > newValues.Length || max < 0) ? newValues.Length : max;

    for (int i = 0; i < loopmax; ++i)
    {
        oldValues[oldValuesStartindex + i] = newValues[newValuesStartindex + i];
    }
}
2013-04-26 18:28:34,347 [8] DEBUG ClassName - Detected tag (000CCC756081) with Exciter Id (0)
2013-04-26 18:28:34,347 [8] DEBUG ClassName - Detected tag is displayable (Unknown: ?56081)
2013-04-26 18:28:34,347 [8] TRACE ClassName - Keys
2013-04-26 18:28:34,347 [8] TRACE ClassName - Address: 000CCC755898
2013-04-26 18:28:34,347 [8] TRACE ClassName - Address: 000CCC756081
2013-04-26 18:28:34,347 [8] TRACE ClassName - Address: 000CCC755A27
2013-04-26 18:28:34,347 [8] TRACE ClassName - Address: 000CCC755B47
2013-04-26 18:28:34,347 [8] TRACE ClassName - Values
2013-04-26 18:28:34,347 [8] TRACE ClassName - Address: 000CCC755898 Name: Scotty McTester
2013-04-26 18:28:34,347 [8] TRACE ClassName - Address: 000CCC756081 Name: ?56081
2013-04-26 18:28:34,347 [8] TRACE ClassName - Address: 000CCC755A27 Name: JDTest1
2013-04-26 18:28:34,347 [8] TRACE ClassName - Address: 000CCC755B47 Name: 33 1
2013-04-26 18:28:34,347 [8] TRACE ClassName - Current tags: Scotty McTester, ?56081, JDTest1, 33 1
2013-04-26 18:28:35,608 [8] DEBUG ClassName - Detected tag (000CCC756081) with Exciter Id (0)
2013-04-26 18:28:35,608 [8] DEBUG ClassName - Detected tag is displayable (Unknown: ?56081)
2013-04-26 18:28:35,608 [8] TRACE ClassName - Keys
2013-04-26 18:28:35,608 [8] TRACE ClassName - Address: 000CCC755898
2013-04-26 18:28:35,608 [8] TRACE ClassName - Address: 000CCC756081
2013-04-26 18:28:35,618 [8] TRACE ClassName - Address: 000CCC755A27
2013-04-26 18:28:35,618 [8] TRACE ClassName - Address: 000CCC755B47
2013-04-26 18:28:35,618 [8] TRACE ClassName - Address: 000CCC756081
2013-04-26 18:28:35,618 [8] TRACE ClassName - Values
2013-04-26 18:28:35,618 [8] TRACE ClassName - Address: 000CCC755898 Name: Scotty McTester
2013-04-26 18:28:35,618 [8] TRACE ClassName - Address: 000CCC756081 Name: ?56081
2013-04-26 18:28:35,648 [8] TRACE ClassName - Address: 000CCC755A27 Name: JDTest1
2013-04-26 18:28:35,648 [8] TRACE ClassName - Address: 000CCC755B47 Name: 33 1
2013-04-26 18:28:35,648 [8] TRACE ClassName - Address: 000CCC756081 Name: ?56081
2013-04-26 18:28:35,648 [8] TRACE ClassName - Current tags: Scotty McTester, ?56081, JDTest1, 33 1, ?56081
var data = new byte[] { 1,2,3 };
var mac = new PhysicalAddress(data);
data[0] = 0;
using System;
using System.Collections.Generic;
using System.Net.NetworkInformation;

class App
{
  static void Main()
  {
    var data = new byte[] { 1,2,3,4 };
    var mac1 = new PhysicalAddress(data);
    var mac2 = new PhysicalAddress(data);
    var dictionary = new Dictionary<PhysicalAddress,string>();
    dictionary[mac1] = "A";
    Console.WriteLine("Has mac1:" + dictionary.ContainsKey(mac1));
    //Console.WriteLine("Has mac2:" + dictionary.ContainsKey(mac2));
    data[0] = 0;
    Console.WriteLine("After modification");
    Console.WriteLine("Has mac1:" + dictionary.ContainsKey(mac1));
    Console.WriteLine("Has mac2:" + dictionary.ContainsKey(mac2));

    dictionary[mac2] = "B";
    foreach (var kvp in dictionary)
      Console.WriteLine(kvp.Key + "=" + kvp.Value);
  }
}