第6回 IComparer<T>とDictionary(IComparer<T>)
.Net1.xの世界ではIComparerというインターフェイスはobjとobjを比較するもので、どうしてもキャストしないといけないものであった。
.Net2.0ではIComparer<T>というものが導入されることになる。
これを利用すると、Dictionary<K, V>の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 : IComparer<商品Key>
{
public int Compare(商品Key x, 商品Key y)
{
int CompareInfo = x.商品コード.CompareTo(y.商品コード);
if (CompareInfo != 0)
{
return CompareInfo;
}
else
{
CompareInfo = x.カラー.CompareTo(y.カラー);
if (CompareInfo != 0)
{
return CompareInfo;
}
else
{
return 0;
}
}
}
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 WindowsApplication1.商品Key
Get
Dim key As 商品Key = New 商品Key()
key.商品コード = Me.商品コード
key.カラー = Me.カラー
Return key
End Get
End Property
End Class
Public Class 商品Comparer
Implements IComparer(Of 商品Key)
Public Function Compare(ByVal x As 商品Key, ByVal y As 商品Key) As Integer Implements System.Collections.Generic.IComparer(Of 商品Key).Compare
Dim CompareInfo As Integer = x.商品コード.CompareTo(y.商品コード)
If (CompareInfo <> 0) Then
Compare = CompareInfo
Exit Function
Else
CompareInfo = x.カラー.CompareTo(y.カラー)
If (CompareInfo <> 0) Then
Compare = CompareInfo
Exit Function
Else
Return 0
End If
End If
End Function
Public Function Equals1(ByVal x As 商品Key, ByVal y As 商品Key) As Boolean Implements System.Collections.Generic.IComparer(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.IComparer(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::IComparer<商品Key^>
{
public:
int Compare(商品Key^ x, 商品Key^ y)
{
int CompareInfo = x->商品コード->CompareTo(y->商品コード);
if (CompareInfo != 0)
{
return CompareInfo;
}
else
{
CompareInfo = x->カラー->CompareTo(y->カラー);
if (CompareInfo != 0)
{
return CompareInfo;
}
else
{
return 0;
}
}
}
bool Equals(商品Key^ x, 商品Key^ y)
{
if (x->商品コード == y->商品コード && x->カラー == y->カラー)
{
return true;
}
else
{
return false;
}
}
int GetHashCode(商品Key^ obj)
{
String^ HashData = String::Format(L"商品コード:{0}\r\nカラー:{1}", obj->商品コード, obj->カラー);
return HashData->GetHashCode();
}
};
商品というクラスのうちの一部が実際のキーで、そのキーが複合キーの場合で、Dictionaryに格納するなどという場合以前までは直感的なコードは組めなかった。(ので今回は1.xソースは割愛)
2.0においてはDictionaryのキー検索ルーチンをIComparer<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->一般商品名 = L"ドラゴンクエツト";
syo->希望小売価格 = gcnew Decimal(5000);
dic->Add(syo->get商品Key(), syo);
商品Key^ 検索キー = gcnew 商品Key();
検索キー->商品コード = "ABC00001";
検索キー->カラー = "Red";
商品^ syo2 = dic->Item[検索キー];
MessageBox::Show(syo2->一般商品名);
利用する側のソースは3言語ともほとんど同一だが、クラスのほうはなかなか特徴が出ていて面白いソースになった。
皆さんはどのソースがお好みだろう。
連載をまとめてあります。
http://blogs.users.gr.jp/naka/category/285.aspx