等価テスト用のラムダ式を指定できるジェネリックの作成に関するこの素敵なブログ投稿を見つけました。これは、次の例のように、標準のクエリ演算子を使用して流暢なIEqualityComparer
結合式を作成するのに非常に役立ちます(有効な自己完結型のLINQPadスクリプト。コピーして貼り付けるだけです!)。
void Main()
{
var outers = new [] {
Tuple.Create("a", "b"),
Tuple.Create("a", "c")
};
var inners = new [] {
Tuple.Create("b", "c"),
Tuple.Create("a", "c")
};
var j2s = outers
.Join(
inners,
outer => outer,
inner => inner,
(outer, inner) => Tuple.Create(outer, inner),
new GenericEqualityComparer<Tuple<string, string>>(
(u, v) => (u.Item1 == v.Item1 && u.Item2 == v.Item2))
)
.Dump("Using Custom Equality Comparer")
;
}
public sealed class GenericEqualityComparer<T> : IEqualityComparer<T>
{
internal Func<T, T, bool> EqualsFunc {get; private set;}
internal Func<T, int> GetHashCodeFunc {get; private set;}
public GenericEqualityComparer(
Func<T, T, bool> equalsFunc,
Func<T, int> getHashCodeFunc = null)
{
if (equalsFunc == null)
throw new ArgumentNullException("equalsFunc");
if (getHashCodeFunc == null)
getHashCodeFunc = (t => 0x1BADF00D);
EqualsFunc = equalsFunc;
GetHashCodeFunc = getHashCodeFunc;
}
public bool Equals(T x, T y)
{
return EqualsFunc(x, y);
}
public int GetHashCode(T obj)
{
return GetHashCodeFunc(obj);
}
}
問題は、これに型推論を使用させるにはどうすればよいかということです。GenericEqualityComparer<Tuple<string, string>>
コンパイラーは、コンパイラーがそれを理解できるはずだと直感的に思っていたにもかかわらず、コンストラクターへの型引数を明示的に述べるように強制されました。明示的な型引数を省略すると、
Using the generic type 'UserQuery.GenericEqualityComparer<T>' requires 1 type arguments
型推論がなければ、次のように匿名型を使用するなど、より広いシナリオのように見えますが、それは絶望的です:
var j3s = outers
.Join(
inners,
outer => new {Left = outer.Item1, Right = outer.Item2},
inner => new {X = inner.Item1, Y = inner.Item2},
(outer, inner) => Tuple.Create(outer, inner),
new GenericEqualityComparer<????????????????>(
(u, v) => (u.Left == v.X && u.Right == v.Y)
)
)
.Dump("Using type inference?")
;