If the key is part of the class then use KeyedCollection.
It is a Dictionary where the key is derived from the object.
Under the covers it is Dictionary. Don't have to repeat the key in the Key and Value.
Why take a chance the key is not the same in the Key as the Value. Don't have to duplicate the same information in memory.
You did not define Category or Categories so used string.
KeyedCollection Class
Indexer to expose the composite key
using System.Collections.ObjectModel;
namespace KeyCollStringString
{
class Program
{
static void Main(string[] args)
{
StringStringO ss1 = new StringStringO("Sall","John");
StringStringO ss2 = new StringStringO("Sall", "John");
if (ss1 == ss2) Console.WriteLine("same");
if (ss1.Equals(ss2)) Console.WriteLine("equals");
// that are equal but not the same I don't override = so I have both features
StringStringCollection stringStringCollection = new StringStringCollection();
// dont't have to repeat the key like Dictionary
stringStringCollection.Add(new StringStringO("Ringo", "Paul"));
stringStringCollection.Add(new StringStringO("Mary", "Paul"));
stringStringCollection.Add(ss1);
//this would thow a duplicate key error
//stringStringCollection.Add(ss2);
//this would thow a duplicate key error
//stringStringCollection.Add(new StringStringO("Ringo", "Paul"));
Console.WriteLine("count");
Console.WriteLine(stringStringCollection.Count.ToString());
// reference by ordinal postion (note the is not the long key)
Console.WriteLine("oridinal");
Console.WriteLine(stringStringCollection[0].GetHashCode().ToString());
// reference by index
Console.WriteLine("index");
Console.WriteLine(stringStringCollection["Mary", "Paul"].GetHashCode().ToString());
Console.WriteLine("foreach");
foreach (StringStringO ssO in stringStringCollection)
{
Console.WriteLine(string.Format("HashCode {0} String1 {1} String2 {2} ", ssO.GetHashCode(), ssO.String1, ssO.String2));
}
Console.WriteLine("sorted by date");
foreach (StringStringO ssO in stringStringCollection.OrderBy(x => x.String1).ThenBy(x => x.String2))
{
Console.WriteLine(string.Format("HashCode {0} String1 {1} String2 {2} ", ssO.GetHashCode(), ssO.String1, ssO.String2));
}
Console.ReadLine();
}
public class StringStringCollection : KeyedCollection<StringStringS, StringStringO>
{
// This parameterless constructor calls the base class constructor
// that specifies a dictionary threshold of 0, so that the internal
// dictionary is created as soon as an item is added to the
// collection.
//
public StringStringCollection() : base(null, 0) { }
// This is the only method that absolutely must be overridden,
// because without it the KeyedCollection cannot extract the
// keys from the items.
//
protected override StringStringS GetKeyForItem(StringStringO item)
{
// In this example, the key is the part number.
return item.StringStringS;
}
// indexer
public StringStringO this[string String1, string String2]
{
get { return this[new StringStringS(String1, String2)]; }
}
}
public struct StringStringS
{ // required as KeyCollection Key must be a single item
// but you don't reaaly need to interact with Int32Int32s
public readonly String String1, String2;
public StringStringS(string string1, string string2) { this.String1 = string1.Trim(); this.String2 = string2.Trim(); }
}
public class StringStringO : Object
{
// implement you properties
public StringStringS StringStringS { get; private set; }
public String String1 { get { return StringStringS.String1; } }
public String String2 { get { return StringStringS.String2; } }
public override bool Equals(Object obj)
{
//Check for null and compare run-time types.
if (obj == null || !(obj is StringStringO)) return false;
StringStringO item = (StringStringO)obj;
return (this.String1 == item.String1 && this.String2 == item.String2);
}
public override int GetHashCode()
{
int hash = 17;
// Suitable nullity checks etc, of course :)
hash = hash * 23 + String1.GetHashCode();
hash = hash * 23 + String1.GetHashCode();
return hash;
}
public StringStringO(string string1, string string2)
{
StringStringS stringStringS = new StringStringS(string1, string2);
this.StringStringS = stringStringS;
}
}
}
}