您要求一个简单的解决方案来解决问题,
GroupBy
+
Where
+
Select
的解决方案完全满足这个要求,但您可能也对高性能和内存效率的解决方案感兴趣。下面是一种实现方式,使用了目前可用的所有工具(.NET 6+)以达到最大的效率:
public static IEnumerable<TSource> UniqueBy<TSource, TKey>(
this IEnumerable<TSource> source,
Func<TSource, TKey> keySelector,
IEqualityComparer<TKey> comparer = default)
{
ArgumentNullException.ThrowIfNull(source);
ArgumentNullException.ThrowIfNull(keySelector);
Dictionary<TKey, (TSource Item, bool Unique)> dictionary = new(comparer);
if (source.TryGetNonEnumeratedCount(out int count))
dictionary.EnsureCapacity(count);
foreach (TSource item in source)
CollectionsMarshal.GetValueRefOrAddDefault(dictionary, keySelector(item),
out bool exists) = exists ? default : (item, true);
foreach ((TSource item, bool unique) in dictionary.Values)
if (unique)
yield return item;
}
TryGetNonEnumeratedCount
+EnsureCapacity
组合在枚举源时可以对内存分配量产生显著影响,如果源是具有已知大小的类型,例如List<T>
。
CollectionsMarshal.GetValueRefOrAddDefault
确保每个键只会被哈希一次,在键具有昂贵的GetHashCode
实现的情况下,这可能会产生影响。
使用示例:
List<MyClass> unique = myClassObject.UniqueBy(x => x.BillId).ToList()
在线演示。
上述UniqueBy
与内置的DistinctBy
LINQ运算符的区别在于,前者完全消除了重复项,而后者保留了每个重复元素的第一个实例。