1

我发现这篇关于创建泛型的可爱博客文章IEqualityComparer让您可以指定用于相等测试的 lambda 表达式。这对于使用标准查询运算符构建流畅 的连接表达式非常有帮助,如下例所示(有效的、自包含的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。我重复一遍:这是一个 hack。

请注意,只有当两个键选择器返回相同的匿名类型时,才能调用 Join。相等比较器只能比较相同类型的两个项目。

于 2012-08-20T21:51:44.710 回答