1

等価テスト用のラムダ式を指定できるジェネリックの作成に関するこの素敵なブログ投稿を見つけました。これは、次の例のように、標準のクエリ演算子を使用して流暢な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?")
;
4

1 に答える 1

1

非ジェネリック クラスに静的ファクトリを作成します。

public static GenericEqualityComparer<T> Create<T>(
    T prototype,
    Func<T, T, bool> equalsFunc,
    Func<T, int> getHashCodeFunc = null) {
       return new GenericEqualityComparer<T>(equalsFunc, getHashCodeFunc);
    }

最初のパラメーターは無視されます。これは、T が無名であっても、コンパイラに T を推論させる方法です。ハックです。

次のように使用します。

UserQuery.GenericEqualityComparer.Create(new {X = 0, Y = 0}, (u, v) => ...)

これにより、u と v を推測できます。繰り返しますが、これはハックです。

Join の呼び出しは、両方のキー セレクターが同じ匿名型を返す場合にのみ機能することに注意してください。等値比較子は、同じ型の 2 つの項目のみを比較できます。

于 2012-08-20T21:51:44.710 に答える