技術解説 .NET 2.0 ジェネリック(Generics) 第6回 IEqualityComparer<T>とDictionary(IEqualityComparer<T>)
2006/01/26
この文書はVisual Studio 2005をベースに記述されています。製品版や、あなたが読んでいる時点では変更されている可能性があります。
第6回 IEqualityComparer<T>とDictionary(IEqualityComparer<T>)
.NET1.xの世界ではIComparerというインターフェイスはobjとobjを比較するもので、どうしてもキャストしないといけないものであった。
.NET2.0ではIComparer<T>というものが導入されることになるが、DictionaryではIEqualityComparer<T>を利用する。
これを利用すると、Dictionary<TKey, TValue>のKeyに独自のクラスを設定したりする場合に非常に便利になる。
まず利用するクラスを下記のように定義する。
下記が実際のクラス定義
public class 商品Key { public string 商品コード; public string カラー; } public class 商品 { public string 商品コード; public string カラー; public string 一般商品名; public decimal 希望小売価格; public 商品Key 商品Key { get { 商品Key key = new 商品Key(); key.商品コード = this.商品コード; key.カラー = this.カラー; return key; } } } public class 商品Comparer : IEqualityComparer<商品Key> { public bool Equals(商品Key x, 商品Key y) { if (x.商品コード == y.商品コード && x.カラー == y.カラー) { return true; } else { return false; } } public int GetHashCode(商品Key obj) { string HashData = string.Format("商品コード:{0}\r\nカラー:{1}", obj.商品コード, obj.カラー); return HashData.GetHashCode(); } }
Public Class 商品Key Public 商品コード As String Public カラー As String End Class Public Class 商品 Public 商品コード As String Public カラー As String Public 一般商品名 As String Public 希望小売価格 As Decimal Public ReadOnly Property 商品Key() As 商品Key Get Dim key As 商品Key = New 商品Key() key.商品コード = Me.商品コード key.カラー = Me.カラー Return key End Get End Property End Class Public Class 商品Comparer Implements IEqualityComparer(Of 商品Key) Public Function Equals1(ByVal x As 商品Key, ByVal y As 商品Key) As Boolean Implements System.Collections.Generic.IEqualityComparer(Of 商品Key).Equals If (x.商品コード = y.商品コード And x.カラー = y.カラー) Then Equals1 = True Else Equals1 = False End If End Function Public Function GetHashCode1(ByVal obj As 商品Key) As Integer Implements System.Collections.Generic.IEqualityComparer(Of 商品Key).GetHashCode Dim HashData As String = String.Format("商品コード:{0}\r\nカラー:{1}", obj.商品コード, obj.カラー) GetHashCode1 = HashData.GetHashCode() End Function End Class
public ref class 商品Key : public System::Object { public: String^ 商品コード; String^ カラー; }; public ref class 商品 : public System::Object { public: String^ 商品コード; String^ カラー; String^ 一般商品名; Decimal^ 希望小売価格; 商品Key^ get商品Key() { 商品Key^ key = gcnew 商品Key(); key->商品コード = this->商品コード; key->カラー = this->カラー; return key; } }; public ref class 商品Comparer : System::Collections::Generic::IEqualityComparer<商品Key^> { public: virtual bool Equals(商品Key^ x, 商品Key^ y) { if (x->商品コード == y->商品コード && x->カラー == y->カラー) { return true; } else { return false; } } virtual int GetHashCode(商品Key^ obj) { String^ HashData = String::Format(L"商品コード:{0}\r\nカラー:{1}", obj->商品コード, obj->カラー); return HashData->GetHashCode(); } };
商品というクラスのうちの一部が実際のキーで、そのキーが複合キーの場合で、Dictionaryに格納するなどという場合以前までは直感的なコードは組めなかった。(ので今回は1.xソースは割愛)
2.0においてはDictionaryのキー検索ルーチンをIEqualityComparer<T>でより柔軟に設計することができる。
これだけのコードを組むと以下のルーチンでDictionary<T>に格納したり抜き出したりということができるようになる。
Dictionary<商品Key, 商品> dic = new Dictionary<商品Key, 商品>(new 商品Comparer()); 商品 syo = new 商品(); syo.商品コード = "ABC00001"; syo.カラー = "Red"; syo.一般商品名 = "ドラゴンクエツト"; syo.希望小売価格 = 5000; dic.Add(syo.商品Key, syo); 商品Key 検索キー = new 商品Key(); 検索キー.商品コード = "ABC00001"; 検索キー.カラー = "Red"; 商品 syo2 = dic[検索キー]; MessageBox.Show(syo2.一般商品名);
Dim dic As Dictionary(Of 商品Key, 商品) = New Dictionary(Of 商品Key, 商品)(New 商品Comparer()) Dim syo As 商品 = New 商品() syo.商品コード = "ABC00001" syo.カラー = "Red" syo.一般商品名 = "ドラゴンクエツト" syo.希望小売価格 = 5000 dic.Add(syo.商品Key, syo) Dim 検索キー As 商品Key = New 商品Key() 検索キー.商品コード = "ABC00001" 検索キー.カラー = "Red" Dim syo2 As 商品 = dic(検索キー) MessageBox.Show(syo2.一般商品名)
Dictionary<商品Key^, 商品^>^ dic = gcnew Dictionary<商品Key^, 商品^>(gcnew 商品Comparer()); 商品^ syo = gcnew 商品(); syo->商品コード = "ABC00001"; syo->カラー = "Red"; syo->一般商品名 = "ドラゴンクエツト"; syo->希望小売価格 = gcnew Decimal(5000); dic->Add(syo->get商品Key(), syo); 商品Key^ 検索キー = gcnew 商品Key(); 検索キー->商品コード = "ABC00001"; 検索キー->カラー = "Red"; 商品^ syo2 = dic->default[検索キー]; MessageBox::Show(syo2->一般商品名);
利用する側のソースは3言語ともほとんど同一だが、クラスのほうはなかなか特徴が出ていて面白いソースになった。
皆さんはどのソースがお好みだろう。