因此,.NET 3.0/3.5为我们提供了许多查询,排序和操作数据的新方法,这要归功于LINQ提供的所有简洁功能.有时,我需要比较没有内置比较运算符的用户定义类型.在许多情况下,比较非常简单 - 比如foo1.key?= foo2.key.我可以使用匿名委托/ lambda函数简单地指定内联比较,而不是为该类型创建新的IEqualityComparer吗?就像是:
var f1 = ..., f2 = ...; var f3 = f1.Except( f2, new IEqualityComparer( (Foo a, Foo b) => a.key.CompareTo(b.key) ) );
我很确定上面的内容实际上并不奏效.我只是不想让整个班级做一些"沉重"的事情只是为了告诉程序如何比较苹果和苹果.
我的MiscUtil库包含一个ProjectionComparer,用于从投影委托构建IComparer
编辑:这是ProjectionEqualityComparer的代码:
using System; using System.Collections.Generic; ////// Non-generic class to produce instances of the generic class, /// optionally using type inference. /// public static class ProjectionEqualityComparer { ////// Creates an instance of ProjectionEqualityComparer using the specified projection. /// ///Type parameter for the elements to be compared ///Type parameter for the keys to be compared, /// after being projected from the elements /// Projection to use when determining the key of an element ///A comparer which will compare elements by projecting /// each element to its key, and comparing keys public static ProjectionEqualityComparerCreate (Func projection) { return new ProjectionEqualityComparer (projection); } /// /// Creates an instance of ProjectionEqualityComparer using the specified projection. /// The ignored parameter is solely present to aid type inference. /// ///Type parameter for the elements to be compared ///Type parameter for the keys to be compared, /// after being projected from the elements /// Value is ignored - type may be used by type inference /// Projection to use when determining the key of an element ///A comparer which will compare elements by projecting /// each element to its key, and comparing keys public static ProjectionEqualityComparerCreate (TSource ignored, Func projection) { return new ProjectionEqualityComparer (projection); } } /// /// Class generic in the source only to produce instances of the /// doubly generic class, optionally using type inference. /// public static class ProjectionEqualityComparer{ /// /// Creates an instance of ProjectionEqualityComparer using the specified projection. /// ///Type parameter for the keys to be compared, /// after being projected from the elements /// Projection to use when determining the key of an element ///A comparer which will compare elements by projecting each element to its key, /// and comparing keys public static ProjectionEqualityComparerCreate (Func projection) { return new ProjectionEqualityComparer (projection); } } /// /// Comparer which projects each element of the comparison to a key, and then compares /// those keys using the specified (or default) comparer for the key type. /// ///Type of elements which this comparer /// will be asked to compare ///Type of the key projected /// from the element public class ProjectionEqualityComparer: IEqualityComparer { readonly Func projection; readonly IEqualityComparer comparer; /// /// Creates a new instance using the specified projection, which must not be null. /// The default comparer for the projected type is used. /// /// Projection to use during comparisons public ProjectionEqualityComparer(Funcprojection) : this(projection, null) { } /// /// Creates a new instance using the specified projection, which must not be null. /// /// Projection to use during comparisons /// The comparer to use on the keys. May be null, in /// which case the default comparer will be used. public ProjectionEqualityComparer(Funcprojection, IEqualityComparer comparer) { if (projection == null) { throw new ArgumentNullException("projection"); } this.comparer = comparer ?? EqualityComparer .Default; this.projection = projection; } /// /// Compares the two specified values for equality by applying the projection /// to each value and then using the equality comparer on the resulting keys. Null /// references are never passed to the projection. /// public bool Equals(TSource x, TSource y) { if (x == null && y == null) { return true; } if (x == null || y == null) { return false; } return comparer.Equals(projection(x), projection(y)); } ////// Produces a hash code for the given value by projecting it and /// then asking the equality comparer to find the hash code of /// the resulting key. /// public int GetHashCode(TSource obj) { if (obj == null) { throw new ArgumentNullException("obj"); } return comparer.GetHashCode(projection(obj)); } }
以下是一个示例用法:
var f3 = f1.Except(f2, ProjectionEqualityComparer.Create(a => a.key));
这是一个简单的助手类,应该做你想要的
public class EqualityComparer: IEqualityComparer { public EqualityComparer(Func cmp) { this.cmp = cmp; } public bool Equals(T x, T y) { return cmp(x, y); } public int GetHashCode(T obj) { return obj.GetHashCode(); } public Func cmp { get; set; } }
你可以像这样使用它:
processed.Union(suburbs, new EqualityComparer((s1, s2) => s1.SuburbId == s2.SuburbId));
我发现在IEnumerable上提供额外的帮助是一种更干净的方法.
见:这个问题
所以你可以:
var f3 = f1.Except( f2, (a, b) => a.key.CompareTo(b.key) );
如果正确定义扩展方法
这个项目做了类似的事情:AnonymousComparer - Linq的lambda比较选择器,它也有LINQ标准查询运算符的扩展.
为什么不是这样的:
public class Comparer: IEqualityComparer { private readonly Func _equalityComparer; public Comparer(Func equalityComparer) { _equalityComparer = equalityComparer; } public bool Equals(T first, T second) { return _equalityComparer(first, second); } public int GetHashCode(T value) { return value.GetHashCode(); } }
然后你可以例如像(如在的情况下做Intersect
的IEnumerable
):
list.Intersect(otherList, new Comparer( (x, y) => x.Property == y.Property));
本Comparer
类可以放在一个公共事业项目,被需要的地方使用.
我现在才看到Sam Saffron的答案(与此非常相似).