Enumerable class contains many overloads with IEqualityComparable<T> argument. Most notable methods are:
Enumerable class
IEqualityComparable<T>
Recently we dealt with simple case:
source. Select( item => new Word { Text = ..., LangID = ..., Properties = ... ... }). Distinct(equality comparer by Text and LangID);
In other words how do you produce a enumeration of distinct words from a enumeration of words, where two words are qualified equal if their Text and LangID are equal?
Text
LangID
It turns out it's cumbersome to implement IEqualityComparer<T> interface (and any other interface in C#), at least it's nothing close to a conciseness of lambda functions.
IEqualityComparer<T>
Here we've decided to step in into framework space and to introduce an API to define simple equality comparers for a class.
We start from the use case:
var wordComparer = KeyEqualityComparer.Null<Word>(). ThenBy(item => item.Text). ThenBy(item => item.LangID); ... source.Select(...).Distinct(wordComparer);
And then proceed to the API:
namespace NesterovskyBros.Linq { using System; using System.Collections; using System.Collections.Generic; /// <summary> /// A equality comparer extensions. /// </summary> public static class KeyEqualityComparer { /// <summary> /// Gets null as equality comparer for a type. /// </summary> /// <typeparam name="T">A type.</typeparam> /// <returns> /// null as equality comparer for a type. /// </returns> public static IEqualityComparer<T> Null<T>() { return null; } /// <summary> /// Creates an equality comparer for a enumeration item. /// </summary> /// <typeparam name="T">A type.</typeparam> /// <param name="source">A source items.</param> /// <param name="keyFunc">A key function.</param> /// <returns> /// null as equality comparer for a type. /// </returns> public static IEqualityComparer<T> EqualityComparerBy<T, K>( this IEnumerable<T> source, Func<T, K> keyFunc) { return new KeyEqualityComparer<T, K>(keyFunc); } /// <summary> /// Creates an equality comparer that uses this comparer as a base. /// </summary> /// <typeparam name="T">A type.</typeparam> /// <typeparam name="K">A key type.</typeparam> /// <param name="equalityComparer">A base equality comparer.</param> /// <param name="keyFunc">A key function.</param> /// <returns> /// An equality comparer that uses this comparer as a base. /// </returns> public static KeyEqualityComparer<T, K> ThenBy<T, K>( this IEqualityComparer<T> equalityComparer, Func<T, K> keyFunc) { return new KeyEqualityComparer<T, K>(keyFunc, equalityComparer); } } /// <summary> /// Equality comparer that uses a function to extract a comparision key. /// </summary> /// <typeparam name="T">A type.</typeparam> /// <typeparam name="K">A key type.</typeparam> public struct KeyEqualityComparer<T, K>: IEqualityComparer<T> { /// <summary> /// Creates an equality comparer. /// </summary> /// <param name="keyFunc">A key function.</param> /// <param name="equalityComparer">A base equality comparer.</param> public KeyEqualityComparer( Func<T, K> keyFunc, IEqualityComparer<T> equalityComparer = null) { KeyFunc = keyFunc; EqualityComparer = equalityComparer; } /// </summary> /// <param name="x">The first object of type T to compare.</param> /// <param name="y">The second object of type T to compare.</param> /// <returns> /// true if the specified objects are equal; otherwise, false. /// </returns> public bool Equals(T x, T y) { return ((EqualityComparer == null) || EqualityComparer.Equals(x, y)) && EqualityComparer<K>.Default.Equals(KeyFunc(x), KeyFunc(y)); } /// <summary> /// Returns a hash code for the specified object. /// </summary> /// <param name="obj"> /// The value for which a hash code is to be returned. /// </param> /// <returns>A hash code for the specified object.</returns> public int GetHashCode(T obj) { var hash = EqualityComparer<K>.Default.GetHashCode(KeyFunc(obj)); if (EqualityComparer != null) { var hash2 = EqualityComparer.GetHashCode(obj); hash ^= (hash2 << 5) + hash2; } return hash; } /// <summary> /// A key function. /// </summary> public readonly Func<T, K> KeyFunc; /// <summary> /// Optional base equality comparer. /// </summary> public readonly IEqualityComparer<T> EqualityComparer; } }
So, now you can easily build simple equality comparers to cache them or instantiate on the fly. This comparers are usually related to property values or their function of source values.
See also LINQ extensions