試想如果能寫成下面的樣子,是不是更簡單優雅:
var p1 = products.Distinct(p => p.ID);
var p2 = products.Distinct(p => p.Name);
使用一個簡單的lambda 作為參數,也符合Linq 一貫的風格。
可通過擴展方法實現:
Distinct 擴展方法
首先,創建一個通用比較的類,實現IEqualityComparer<T> 接口:
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Linq;
public class CommonEqualityComparer<T, V> : IEqualityComparer<T>
{
private Func<T, V> keySelector;
public CommonEqualityComparer(Func<T, V> keySelector)
{
this.keySelector = keySelector;
}
public bool Equals(T x, T y)
{
return EqualityComparer<V>.Default.Equals(keySelector(x), keySelector(y));
}
public int GetHashCode(T obj)
{
return EqualityComparer<V>.Default.GetHashCode(keySelector(obj));
}
}
第17 行,用到了EqualityComparer<T> 類,本文最後有簡要說明。
借助上面這個類,Distinct 擴展方法就很好寫了:
1
public static class DistinctExtensions
{
public static IEnumerable<T> Distinct<T, V>(this IEnumerable<T> source, Func<T, V> keySelector)
{
return source.Distinct(new CommonEqualityComparer<T, V>(keySelector));
}
}
呵呵,簡單吧!
Distinct 使用示例
根據ID :
var data1 = new Person[] {
new Person{ ID = 1, Name = "鶴沖天"},
new Person{ ID = 1, Name = "ldp"}
};
var ps1 = data1
.Distinct(p => p.ID)
.ToArray();
根據Name:
var data2 = new Person[] {
new Person{ ID = 1, Name = "鶴沖天"},
new Person{ ID = 2, Name = "鶴沖天"}
};
var ps2 = data2
.Distinct(p => p.Name)
.ToArray();
看了回復後,我做了些改進,推薦使用下面的方式:
改進
回復中有朋友提到“不區分大小寫地排除重復的字符串”,也不難實現,只需要把上面的代碼改進下就OK:
CommonEqualityComparer<T, V> 類:
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Linq;
public class CommonEqualityComparer<T, V> : IEqualityComparer<T>
{
private Func<T, V> keySelector;
private IEqualityComparer<V> comparer;
public CommonEqualityComparer(Func<T, V> keySelector, IEqualityComparer<V> comparer)
{
this.keySelector = keySelector;
this.comparer = comparer;
}
public CommonEqualityComparer(Func<T, V> keySelector)
: this(keySelector, EqualityComparer<V>.Default)
{ }
public bool Equals(T x, T y)
{
return comparer.Equals(keySelector(x), keySelector(y));
}
public int GetHashCode(T obj)
{
return comparer.GetHashCode(keySelector(obj));
}
}
Distinct 擴展方法:
public static class DistinctExtensions
{
public static IEnumerable<T> Distinct<T, V>(this IEnumerable<T> source, Func<T, V> keySelector)
{
return source.Distinct(new CommonEqualityComparer<T, V>(keySelector));
}
public static IEnumerable<T> Distinct<T, V>(this IEnumerable<T> source, Func<T, V> keySelector, IEqualityComparer<V> comparer)
{
return source.Distinct(new CommonEqualityComparer<T, V>(keySelector, comparer));
}
}
借助可選參數,這兩個擴展方法也可以合成一個:
public static IEnumerable<T> Distinct<T, V>(this IEnumerable<T> source, Func<T, V> keySelector,
IEqualityComparer<V> comparer = EqualityComparer<V>.Default)
{
return source.Distinct(new CommonEqualityComparer<T, V>(keySelector, comparer));
}
(同樣,CommonEqualityComparer<T, V>類的兩個構造函數也可以合二為一)
使用示例:
var data3 = new Person[] {
new Person{ ID = 1, Name = "LDP"},
new Person{ ID = 2, Name = "ldp"}
};
var ps3 = data3
.Distinct(p => p.Name, StringComparer.CurrentCultureIgnoreCase)
.ToArray();
EqualityComparer<T> 類 簡要說明
EqualityComparer<T>為IEqualityComparer<T> 泛型接口的實現提供基類,它在.net 4 中有五個重要的子類,見下圖:
這五個子類分別用不同類型數據的相等性比較,從類名我們可以略知一二。
這五個子類都是內部類(internal),不能直接訪問,EqualityComparer<T> 類提供一個簡單的屬性Default。EqualityComparer<T> 會根據傳入的T 的類型,加載不同的子類,並會予以緩存提高性能。