因此,由于LINQ提供的所有简洁功能,.NET 3.0 / 3.5为我们提供了许多查询,排序和操作数据的新方法。有时,我需要比较没有内置比较运算符的用户定义类型。在许多情况下,比较非常简单-类似于foo1.key?= foo2.key。除了可以为类型创建新的IEqualityComparer之外,我还可以简单地使用匿名委托/ lambda函数内联指定比较吗?就像是:
var f1 = ..., f2 = ...; var f3 = f1.Except( f2, new IEqualityComparer( (Foo a, Foo b) => a.key.CompareTo(b.key) ) );
我很确定上述方法实际上无效。我只是不想让整个班级都变得“繁重”,而只是告诉程序如何将苹果与苹果进行比较。
我的MiscUtil库包含一个ProjectionComparer,可从一个投影委托中构建一个IComparer 。使ProjectionEqualityComparer进行相同的操作将需要10分钟的时间。
编辑:这是ProjectionEqualityComparer的代码:
using System; using System.Collections.Generic; /// <summary> /// Non-generic class to produce instances of the generic class, /// optionally using type inference. /// </summary> public static class ProjectionEqualityComparer { /// <summary> /// Creates an instance of ProjectionEqualityComparer using the specified projection. /// </summary> /// <typeparam name="TSource">Type parameter for the elements to be compared</typeparam> /// <typeparam name="TKey">Type parameter for the keys to be compared, /// after being projected from the elements</typeparam> /// <param name="projection">Projection to use when determining the key of an element</param> /// <returns>A comparer which will compare elements by projecting /// each element to its key, and comparing keys</returns> public static ProjectionEqualityComparer<TSource, TKey> Create<TSource, TKey>(Func<TSource, TKey> projection) { return new ProjectionEqualityComparer<TSource, TKey>(projection); } /// <summary> /// Creates an instance of ProjectionEqualityComparer using the specified projection. /// The ignored parameter is solely present to aid type inference. /// </summary> /// <typeparam name="TSource">Type parameter for the elements to be compared</typeparam> /// <typeparam name="TKey">Type parameter for the keys to be compared, /// after being projected from the elements</typeparam> /// <param name="ignored">Value is ignored - type may be used by type inference</param> /// <param name="projection">Projection to use when determining the key of an element</param> /// <returns>A comparer which will compare elements by projecting /// each element to its key, and comparing keys</returns> public static ProjectionEqualityComparer<TSource, TKey> Create<TSource, TKey> (TSource ignored, Func<TSource, TKey> projection) { return new ProjectionEqualityComparer<TSource, TKey>(projection); } } /// <summary> /// Class generic in the source only to produce instances of the /// doubly generic class, optionally using type inference. /// </summary> public static class ProjectionEqualityComparer<TSource> { /// <summary> /// Creates an instance of ProjectionEqualityComparer using the specified projection. /// </summary> /// <typeparam name="TKey">Type parameter for the keys to be compared, /// after being projected from the elements</typeparam> /// <param name="projection">Projection to use when determining the key of an element</param> /// <returns>A comparer which will compare elements by projecting each element to its key, /// and comparing keys</returns> public static ProjectionEqualityComparer<TSource, TKey> Create<TKey>(Func<TSource, TKey> projection) { return new ProjectionEqualityComparer<TSource, TKey>(projection); } } /// <summary> /// 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. /// </summary> /// <typeparam name="TSource">Type of elements which this comparer /// will be asked to compare</typeparam> /// <typeparam name="TKey">Type of the key projected /// from the element</typeparam> public class ProjectionEqualityComparer<TSource, TKey> : IEqualityComparer<TSource> { readonly Func<TSource, TKey> projection; readonly IEqualityComparer<TKey> comparer; /// <summary> /// Creates a new instance using the specified projection, which must not be null. /// The default comparer for the projected type is used. /// </summary> /// <param name="projection">Projection to use during comparisons</param> public ProjectionEqualityComparer(Func<TSource, TKey> projection) : this(projection, null) { } /// <summary> /// Creates a new instance using the specified projection, which must not be null. /// </summary> /// <param name="projection">Projection to use during comparisons</param> /// <param name="comparer">The comparer to use on the keys. May be null, in /// which case the default comparer will be used.</param> public ProjectionEqualityComparer(Func<TSource, TKey> projection, IEqualityComparer<TKey> comparer) { if (projection == null) { throw new ArgumentNullException("projection"); } this.comparer = comparer ?? EqualityComparer<TKey>.Default; this.projection = projection; } /// <summary> /// 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. /// </summary> 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)); } /// <summary> /// 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. /// </summary> public int GetHashCode(TSource obj) { if (obj == null) { throw new ArgumentNullException("obj"); } return comparer.GetHashCode(projection(obj)); } }
这是一个示例用法:
var f3 = f1.Except(f2, ProjectionEqualityComparer<Foo>.Create(a => a.key));