Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p>Dictionary expects immutable object as a key, with a stable GetHashCode / Equals implementation. This means that after object is placed into dictionary, value returned by GetHashCode should not change, and any changes made to this object should not affect Equals method.</p> <p>Although PhysicalAddress class was designed immutable, it still contains a few extension points, where its immutability is flawed. </p> <p>First, it can be changed through input byte array, which is not copied but passed by reference, like this:</p> <pre><code>var data = new byte[] { 1,2,3 }; var mac = new PhysicalAddress(data); data[0] = 0; </code></pre> <p>Second, PhysicalAddress is not a sealed class, and can be changed by derived implementation through overriding Constructor / GetHashCode / Equals methods. But this use case looks more like a hack, so we will ignore it, as well as modifications through reflection.</p> <p>Your situation can only be achieved by first placing PhysicalAddress object into dictionary, and then modifying its source byte array, and then wrapping it into new PhysicalAddress instance.</p> <p>Luckily, PhysicalAddress' GetHashCode implementation computes hash only once, and if same instance is modified, it is still placed into same dictionary bucket, and located again by Equals.</p> <p>But, if source byte array is passed into another instance of PhysicalAddress, where hash was not yet computed - hash is recomputed for new byte[] value, new bucket is located, and duplicate is inserted into dictionary. In rare cases, same bucket can be located from new hash, and again, no duplicate is inserted.</p> <p>Here's the code which reproduces the problem:</p> <pre><code>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&lt;PhysicalAddress,string&gt;(); 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); } } </code></pre> <p>Note the commented line - if we will uncomment it, "ContainsKey" method will precompute hash for mac2, and it will be the same even after modification.</p> <p>So my recommendation is to locate piece of code which generates PhysicalAddress instances, and create new byte array copy for each constructor call.</p>
    singulars
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. VO
      singulars
      1. This table or related slice is empty.
    2. VO
      singulars
      1. This table or related slice is empty.
    3. VO
      singulars
      1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload