Reputation: 145
I am working with a list of dictionaries containing string arrays. The dictionaries are defined/filled via a loop over a DataTable
. In the following code test evaluates to false (twice), can somebody tell me why?
List<Dictionary<string[], int>> mydix = new List<Dictionary<string[], int>>();
mydix.Add(new Dictionary<string[], int>());
mydix.Add(new Dictionary<string[], int>());
mydix.Add(new Dictionary<string[], int>());
string[] s = {"tree"};
mydix[1].Add(s, 1);
bool test = mydix[1].ContainsKey(s); // This evaluates to true, which I understand
var entry= mydix[1][s]; // This is 1
DataTable dt=new DataTable();
dt.Columns.Add("test");
dt.Rows.Add(new string[] {"key"});
mydix[2].Add(dt.Rows[0].ItemArray.Select(x => x.ToString()).ToArray(), 2);
test = mydix[2].ContainsKey(new string[] { "key" }); // Why does this evaluate to false?
// Here is an example with an array with two elements
DataTable dt2 = new DataTable();
dt2.Columns.Add("test");
dt2.Columns.Add("test2");
string[] t={"tree1","tree2"};
dt2.Rows.Add(t);
mydix[0].Add(dt2.Rows[0].ItemArray.Select(x => x.ToString()).ToArray(), 3);
test = mydix[0].ContainsKey(t); // Why does this evaluate to false?
Upvotes: 3
Views: 2024
Reputation: 44921
The problem is that the string array you are using as the key to the dictionary does object comparison, not content comparison.
In order to support this type of data as a key, the easiest solution is to use an IEqualityComparer.
First, create the comparer (this is a sample; yours will need additional sanity checking and logic):
private class ArrayComparer : IEqualityComparer<string[]>
{
public bool Equals(string[] item1, string[] item2)
{
if (item1[0] == item2[0])
{
return true;
}
else
{
return false;
}
}
public int GetHashCode(string[] item)
{
return item[0].GetHashCode();
}
Then, change the instantiation of your dictionaries to use this new comparer:
mydix.Add(new Dictionary<string[], int>(new ArrayComparer()));
mydix.Add(new Dictionary<string[], int>(new ArrayComparer()));
mydix.Add(new Dictionary<string[], int>(new ArrayComparer()));
Once you have done this, both tests will return true.
Upvotes: 1
Reputation: 8656
Hopefully someone will correct me if I'm wrong, but it's my understanding that when you call ContainsKey
, the Dictionary
has a private method (exploring in dotPeek), which runs to decide wether the objects you're comparing are equal or not.
Depending on what type you're using for the key, a different equality comparison will occur, based on various implementations of IEqualityComparer
, this way the most appropriate comparison can be run, based on the types you wish to compare.
You're using string arrays as the keys, so you're essentially checking the equality of the array objects themselves, not their contents. So it's entirely correct that your ContainsKey
is returning false, you aren't asking your Dictionary
if it contains the same array as a key, you're asking it if it contains a different array, which happens to contain the same contents.
The IEqualityComparer
GetHashCode
method in this case (an array), will return a hash based on the reference of the object, not the contents.
If you wanted this behaviour, the magic Mr Skeet has written a custom IEqualityComparer<T>
for arrays in this post:
Upvotes: 1