我可以指定我的显式types比较内联?
所以.NET 3.0 / 3.5为我们提供了很多新的方法来查询,sorting和操作数据,这要感谢LINQ提供的所有整齐的函数。 有时,我需要比较没有内置比较运算符的用户定义types。 在许多情况下,比较非常简单 – 例如foo1.key?= foo2.key。 而不是创build一个新的IEqualityComparer的types,我可以简单地指定使用匿名委托/ lambda函数内联比较? 就像是:
var f1 = ..., f2 = ...; var f3 = f1.Except( f2, new IEqualityComparer( (Foo a, Foo b) => a.key.CompareTo(b.key) ) );
我很确定上述不实际工作。 我只是不想为了告诉程序如何将苹果与苹果进行比较,而不得不像整个class级那样做一些“沉重的事情”。
我的MiscUtil库包含一个ProjectionComparer来从投影委托构build一个IComparer <T>。 使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));
这里是一个简单的助手类,应该做你想做的
public class EqualityComparer<T> : IEqualityComparer<T> { public EqualityComparer(Func<T, T, bool> 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<T, T, bool> cmp { get; set; } }
你可以像这样使用它:
processed.Union(suburbs, new EqualityComparer<Suburb>((s1, s2) => s1.SuburbId == s2.SuburbId));
我发现IEnumerable提供额外的帮助是一个更干净的方式来做到这一点。
看: 这个问题
所以你可以有:
var f3 = f1.Except( f2, (a, b) => a.key.CompareTo(b.key) );
如果您正确定义扩展方法
这个项目做了类似的事情: AnonymousComparer – Linq的lambda比较select器 ,它也有LINQ标准查询操作符的扩展。
为什么不是这样的:
public class Comparer<T> : IEqualityComparer<T> { private readonly Func<T, T, bool> _equalityComparer; public Comparer(Func<T, T, bool> equalityComparer) { _equalityComparer = equalityComparer; } public bool Equals(T first, T second) { return _equalityComparer(first, second); } public int GetHashCode(T value) { return value.GetHashCode(); } }
然后你可以做例如类似的东西(例如,在IEnumerable<T>
中Intersect
的情况下):
list.Intersect(otherList, new Comparer<T>( (x, y) => x.Property == y.Property));
Comparer
类可以放在一个实用程序项目中,并在需要的地方使用。
我现在才看到萨姆·萨弗龙的答案(这个和这个非常相似)。
对于小套,你可以做:
f3 = f1.Where(x1 => f2.All(x2 => x2.key != x1.key));
对于大集合,你会想要更有效率的search,如:
var tmp = new HashSet<string>(f2.Select(f => f.key)); f3 = f1.Where(f => tmp.Add(f.key));
但是,在这里, 键的Type
必须实现IEqualityComparer
(上面我认为这是一个string
)。 所以,这并没有真正回答你在这种情况下使用lambda的问题,但它确实使用了更less的代码,然后是一些答案。
您可能会依赖优化程序并缩短第二个解决scheme:
f3 = f1.Where(x1 => (new HashSet<string>(f2.Select(x2 => x2.key))).Add(x1.key));
但是,我还没有运行testing,知道它是否以相同的速度运行。 而那一个class轮可能太聪明,不能保持。