LinqとIEqualityComparerへの疑問
- 2009-08-07
Distinctの引数はラムダ式でのselectorを受け付けてくれない。IEqualityComparerだけなので、抽出のためにわざわざ外部にIEqualityComparerを実装したクラスを作る必要がある。それって、面倒くさいし分かり辛いし、何でここだけ古くさいような仕様なのだろう。C#3.0っぽくない。しょうがないので、単純ですけど汎用的に使えるようなものを作ってみた。
// IEqualityComparer<T>の実装が面倒なのでセレクタ的なものはこれで賄う
public class CompareSelector<T, TKey> : IEqualityComparer<T>
{
private Func<T, TKey> selector;
public CompareSelector(Func<T, TKey> selector)
{
this.selector = selector;
}
public bool Equals(T x, T y)
{
return selector(x).Equals(selector(y));
}
public int GetHashCode(T obj)
{
return selector(obj).GetHashCode();
}
}
class MyClass
{
public int MyProperty { get; set; }
}
static void Main(string[] args)
{
// このクラスのMyPropertyで重複除去したい
var mc1 = new MyClass { MyProperty = 3 };
var mc2 = new MyClass { MyProperty = 3 };
var array = new[] { mc1, mc2 };
var r1 = array.Distinct().Count();
Console.WriteLine(r1); // 勿論2です
// 比較用のIEqualityComparer<T>インスタンスを渡す
var r2 = array
.Distinct(new CompareSelector<MyClass, int>(mc => mc.MyProperty))
.Count();
Console.WriteLine(r2); // 1です
}
newするから、型を書かなければいけなくてね、記述量が多くて嫌だ。重たい重たい。C#3.0ってのは、もっとライトウェイトじゃなきゃダメなんだ。推論!型推論!しょうがないので、Distinctそのものに拡張メソッドを定義すれば……
public static class ExtensionMethods
{
public static IEnumerable<T> Distinct<T, TKey>(this IEnumerable<T> source, Func<T, TKey> selector)
{
return source.Distinct(new CompareSelector<T, TKey>(selector));
}
}
var r3 = array.Distinct(mc => mc.MyProperty).Count();
Console.WriteLine(r3); // 1になる
ラムダ式で書けるようになる。この調子でIEqualityComparerを使ってるメソッドの全てに拡張メソッドを定義すれば問題なし。しかし準備が面倒。このことは、ForEachが搭載されないことと並ぶLinq最大の謎だと私は思っているのですけど、どうなんでしょうか。何か理由があるのかなあ。とても気になるのだけど……。