2008-03-24

CompositeKey<T,K>

Last week a generic Dictionary was needed to organize a list of values.
The normal use was Dictionary<T,M> where T is the type of the key class, and M is the type of object.
But I was looking a more complex key: not a Guid, it was 2 Guid fields.
THe trouble was:
I had a List<MYCompositeClass>()
MYCompositeClass had 3 values (two of them was the key (KeyA,KeyB)).
To solve this, I created a CompositeKeY(T,K) (T y K is the types of KeyA and KeyB) and I created a Dictionary<CompositeKey<T,K>,List<M>>
If you use ContainsKey with a object generated based on MyCompositeClass, then it didn't work because the seach in the Dictionary<> was by HashCode, not by values of fields.
We need to overrides Equals and GetHashCode (well, this is the shortest method, see below for the longest )

public class CompositeKeY<T, K>
{

private T keyA;
private K keyB;

public T KeyA
{
get{ return keyA;}
set{keyA = value;}
}
public K KeyB
{
get{return keyB;}
set{keyB = value;}
}

public CompositeKeY(T keyA, K keyB)
{
this.keyA = keyA;
this.keyB = keyB;
}

public override bool Equals(object obj)
{
CompositeKeY<T, K> objCompare = obj as CompositeKeY<T, K>;
bool equal = false;
if (objCompare != null)
{
if (this.KeyA.Equals(objCompare.KeyA) && this.KeyB.Equals(objCompare.KeyB))
{
equal = true;
}
}
return equal;
}
public override int GetHashCode()
{
return KeyA.GetHashCode() ^ KeyB.GetHashCode();
}

}

Now we can create
class MYCompositeClass
{
public Guid KeyA;
public Guid KeyB;
public String Value;
}

Dictionary<CompositeKey<Guid,Guid>,List<String>> myDictionary = new Dictionary<CompositeKey<Guid,Guid>,List<String>>();
foreach (MyCompositeClass element in ListOfMyCompositeClass)
{
CompositeKey<Guid,Guid> key = new CompositeKey<Guid,Guid>(element.KeyA,element.KeyB);
if(myDictionary.ContainsKey(key))
{
myDictionary.Add(key,new List<String>);
}
myDictionary[key].Add(element.Value);
}

And It's works. If you removes the override of GetHashCode(), then it doesn't work.

Another method to do the same was using a IEqualityComparer class.

public class CompositeKeyEqualityComparer<T, K> : IEqualityComparer<CompositeKey<T, K>>
{
public bool Equals(CompositeKey<T, K> x, CompositeKey<T, K> y)
{
return Equals(x.KeyA, y.KeyA) && Equals(x.KeyB, y.KeyB);
}

public int GetHashCode(CompositeKey<T, K> obj)
{
return obj.KeyA.GetHashCode() ^ obj.KeyB.GetHashCode();
}

}

Then you can removes el overrides, but you must say to the Dictionary's constructor the IEqualityComparer must use.

Dictionary<CompositeKey<Guid,Guid>,List<String>> myDictionary = new Dictionary<CompositeKey<Guid,Guid>,List<String>>(new CompositeKeyEqualityComparer<Guid,Guid>());
foreach (MyCompositeClass element in ListOfMyCompositeClass)
{
CompositeKey<Guid,Guid> key = new CompositeKey<Guid,Guid>(element.KeyA,element.KeyB);
if(myDictionary.ContainsKey(key))
{
myDictionary.Add(key,new List<String>);
}
myDictionary[key].Add(element.Value);
}

There was at least two solucions to my problem (I didn't more to my work).

No comments: