I have some objects in List, let\'s say List
and MyClass has several properties. I would like to create an index of the list based on 3 properti
Two approaches immediately spring to mind:
Do as Kevin suggested and write a struct that will serve as your key. Be sure to make this struct implement IEquatable<TKey>
and to override its Equals
and GetHashCode
methods*.
Write a class that utilizes nested dictionaries internally. Something like: TripleKeyDictionary<TKey1, TKey2, TKey3, TValue>
... this class would internally have a member of type Dictionary<TKey1, Dictionary<TKey2, Dictionary<TKey3, TValue>>>
, and would expose methods such as this[TKey1 k1, TKey2 k2, TKey3 k3]
, ContainsKeys(TKey1 k1, TKey2 k2, TKey3 k3)
, etc.
*A word on whether overriding the Equals
method is necessary: while it's true that the Equals
method for a struct compares the value of each member by default, it does so by using reflection -- which inherently entails performance costs -- and is therefore not a very appropriate implementation for something that is meant to be used as a key in a dictionary (in my opinion, anyway). According to the MSDN documentation on ValueType.Equals:
The default implementation of the Equals method uses reflection to compare the corresponding fields of obj and this instance. Override the Equals method for a particular type to improve the performance of the method and more closely represent the concept of equality for the type.
May I suggest an alternative - a anonymous object. It's the same we use in GroupBy LINQ method with multiple keys.
var dictionary = new Dictionary<object, string> ();
dictionary[new { a = 1, b = 2 }] = "value";
It may looks strange, but I've benchmarked Tuple.GetHashCode and new{ a = 1, b = 2 }.GetHashCode methods and the anonymous objects wins on my machine on .NET 4.5.1:
Object - 89,1732 ms for 10000 calls in 1000 cycles
Tuple - 738,4475 ms for 10000 calls in 1000 cycles
The best way I could think of is to create a CompositeKey struct and make sure to override the GetHashCode() and Equals() methods in order to ensure speed and accuracy when working with the collection:
class Program
{
static void Main(string[] args)
{
DateTime firstTimestamp = DateTime.Now;
DateTime secondTimestamp = firstTimestamp.AddDays(1);
/* begin composite key dictionary populate */
Dictionary<CompositeKey, string> compositeKeyDictionary = new Dictionary<CompositeKey, string>();
CompositeKey compositeKey1 = new CompositeKey();
compositeKey1.Int1 = 11;
compositeKey1.Int2 = 304;
compositeKey1.DateTime = firstTimestamp;
compositeKeyDictionary[compositeKey1] = "FirstObject";
CompositeKey compositeKey2 = new CompositeKey();
compositeKey2.Int1 = 12;
compositeKey2.Int2 = 9852;
compositeKey2.DateTime = secondTimestamp;
compositeKeyDictionary[compositeKey2] = "SecondObject";
/* end composite key dictionary populate */
/* begin composite key dictionary lookup */
CompositeKey compositeKeyLookup1 = new CompositeKey();
compositeKeyLookup1.Int1 = 11;
compositeKeyLookup1.Int2 = 304;
compositeKeyLookup1.DateTime = firstTimestamp;
Console.Out.WriteLine(compositeKeyDictionary[compositeKeyLookup1]);
CompositeKey compositeKeyLookup2 = new CompositeKey();
compositeKeyLookup2.Int1 = 12;
compositeKeyLookup2.Int2 = 9852;
compositeKeyLookup2.DateTime = secondTimestamp;
Console.Out.WriteLine(compositeKeyDictionary[compositeKeyLookup2]);
/* end composite key dictionary lookup */
}
struct CompositeKey
{
public int Int1 { get; set; }
public int Int2 { get; set; }
public DateTime DateTime { get; set; }
public override int GetHashCode()
{
return Int1.GetHashCode() ^ Int2.GetHashCode() ^ DateTime.GetHashCode();
}
public override bool Equals(object obj)
{
if (obj is CompositeKey)
{
CompositeKey compositeKey = (CompositeKey)obj;
return ((this.Int1 == compositeKey.Int1) &&
(this.Int2 == compositeKey.Int2) &&
(this.DateTime == compositeKey.DateTime));
}
return false;
}
}
}
An MSDN article on GetHashCode():
http://msdn.microsoft.com/en-us/library/system.object.gethashcode.aspx