**Linq 的distinct 常用于對數組、集合的去重操作。**
**一、對于數值型數組或集合去重直接Distinct()就可以。**
new List<int>() { 1, 2, 3, 4, 3, 2 }.Distinct().ToList().ForEach(o => Console.WriteLine(o));
**二、對于自定義類型的集合,直接Distinct往往得不到我們想要的結果**
//例子:
public class Food
{
public int foodId { get; set; }
public string foodName { get; set; }
public override string ToString()
{
return string.Format("{0}\t{1}", foodId.ToString(), foodName.ToString());
}
}
private List<Food> FoodInit()
{
List<Food> fl = new List<Food>();
fl.Add(new Food { foodId = 1, foodName = "rice" });
fl.Add(new Food { foodId = 2, foodName = "noodle" });
fl.Add(new Food { foodId = 1, foodName = "rice" });
return fl;
}
//調用:
FoodInit().Distinct().ToList().ForEach(o => Console.WriteLine(o));
//結果:
//1 rice
//2 noodle
//1 rice
******//解決方法:******
**//方法1. 使用匿名類**
FoodInit().Select(o => new { id = o.foodId, name = o.foodName }).Distinct().ToList().ForEach(o => Console.WriteLine(o));
//結果:
//{ id = 1, name = rice }
//{ id = 2, name = noodle }
**//方法2. 重寫自定義類的Equals,GetHashCode方法****
public class Food : IEquatable<Food>
{
public int foodId { get; set; }
public string foodName { get; set; }
public override string ToString()
{
return string.Format("{0}\t{1}", foodId.ToString(), foodName.ToString());
}
public bool Equals(Food obj)
{
if (obj == null || GetType() != obj.GetType())
return false;
Food foodItem = obj as Food;
return (foodItem.foodId == this.foodId); //規則可以按具體需要,例如 ID 一樣則視為相等
}
public override int GetHashCode()
{
return foodId.GetHashCode(); //注意:按自定義的規則,HashCode相同,才會再去比較 Equals
}
}
//調用
FoodInit().Distinct().ToList().ForEach(o => Console.WriteLine(o));
//結果:
//1 rice
//2 noodle
**//我們可參考 MSDN 上的例子**
public class Product : IEquatable<Product>
{
public string Name { get; set; }
public int Code { get; set; }
public bool Equals(Product other)
{
//Check whether the compared object is null.
if (Object.ReferenceEquals(other, null)) return false;
//Check whether the compared object references the same data.
if (Object.ReferenceEquals(this, other)) return true;
//Check whether the products' properties are equal.
return Code.Equals(other.Code) && Name.Equals(other.Name);
}
// If Equals() returns true for a pair of objects
// then GetHashCode() must return the same value for these objects.
public override int GetHashCode()
{
//Get hash code for the Name field if it is not null.
int hashProductName = Name == null ? 0 : Name.GetHashCode();
//Get hash code for the Code field.
int hashProductCode = Code.GetHashCode();
//Calculate the hash code for the product.
return hashProductName ^ hashProductCode;
}
}
**// 方法3: 實現類的IEqualityComparer接口**
public class Compare : IEqualityComparer<Food>
{
public bool Equals(Food x, Food y)
{
return x.foodId == y.foodId;//可以自定义去重规则,此处将FoodId相同的就相等
}
public int GetHashCode(Food obj)
{
return obj.foodId.GetHashCode();//注意,按自定義去重規則
}
}
//調用:
FoodInit().Distinct(new Compare()).ToList().ForEach(s => Console.WriteLine(s.ToString()));
//結果:
//1 rice
//2 noodle
**//但這樣仍不方便,每個自定義類都需要去寫。我們可以寫一個通用的DISTINCT擴展方法****
public static class DistinctExtensions
{
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));
}
}
public static IEnumerable<T> Distinct<T, V>(this IEnumerable<T> source, Func<T, V> keySelector)
{
return source.Distinct(new CommonEqualityComparer<T, V>(keySelector));
}
}
//調用
FoodInit().Distinct(p => p.foodId).ToList().ForEach(o => Console.WriteLine(o)); 或
FoodInit().Distinct(p => p.foodName).ToList().ForEach(o => Console.WriteLine(o));
//結果
//1 rice
//2 noodle
//網上還有一個方法
public class LambdaComparer<T> : IEqualityComparer<T>
{
private readonly Func<T, T, bool> _lambdaComparer;
private readonly Func<T, int> _lambdaHash;
public LambdaComparer(Func<T, T, bool> lambdaComparer)
: this(lambdaComparer, EqualityComparer<T>.Default.GetHashCode)
{
}
public LambdaComparer(Func<T, T, bool> lambdaComparer, Func<T, int> lambdaHash)
{
if (lambdaComparer == null)
throw new ArgumentNullException("lambdaComparer");
if (lambdaHash == null)
throw new ArgumentNullException("lambdaHash");
_lambdaComparer = lambdaComparer;
_lambdaHash = lambdaHash;
}
public bool Equals(T x, T y)
{
return _lambdaComparer(x, y);
}
public int GetHashCode(T obj)
{
return _lambdaHash(obj);
}
}
很巧妙的採用了泛型委託的方式,實現只需要定義一個類實現IEqualityComparer<TSource>接口,Equals、GetHashCode的實現,由傳入的委託方法決定,接下來就簡單了
var newList3 = list.Distinct(new LambdaComparer<User>((a, b) => a.Id == b.Id && a.Name == b.Name, obj => obj.ToString().GetHashCode())).ToList();
關于Enumerable distinct 的學習筆記
于 2020-12-13 21:24:23 首次发布