国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發設計 > 正文

linqdistinct不夠用了!

2019-11-14 14:04:07
字體:
來源:轉載
供稿:網友

問題引出:在實際中遇到一個問題,要進行集合去重,集合內存儲的是引用類型,需要根據id進行去重。這個時候linq 的distinct 就不夠用了,對于引用類型,它直接比較地址。測試數據如下:

    class Person    {        public int ID { get; set; }        public string Name { get; set; }    }    List<Person> list = new List<Person>()    {         new Person(){ID=1,Name="name1"},         new Person(){ID=1,Name="name1"},         new Person(){ID=2,Name="name2"},         new Person(){ID=3,Name="name3"}                    }; 

 

我們需要根據Person 的 ID 進行去重。當然使用linq Distinct 不滿足,還是有辦法實現的,通過GroupBy先分一下組,再取第一個數據即可。例如:

list.GroupBy(x => x.ID).Select(x => x.FirstOrDefault()).ToList()

通常通過GroupBy去實現也是可以的,畢竟在內存操作還是很快的。但這里我們用別的方式去實現,并且找到最好的實現方式。

 

一、通過IEqualityComparer接口

IEnumerable<T> 的擴展方法 Distinct 定義如下:

public static IEnumerable<TSource> Distinct<TSource>(this IEnumerable<TSource> source);public static IEnumerable<TSource> Distinct<TSource>(this IEnumerable<TSource> source, IEqualityComparer<TSource> comparer);

可以看到,Distinct方法有一個參數為 IEqualityComparer<T> 的重載。該接口的定義如下:

// 類型參數 T: 要比較的對象的類型。public interface IEqualityComparer<T>{    bool Equals(T x, T y);    int GetHashCode(T obj);}

通過實現這個接口我們就可以實現自己的比較器,定義自己的比較規則了。

這里有一個問題,IEqualityComparer<T> 的 T 是要比較的對象的類型,在這里就是 Person,那這里如何去獲得 Person 的屬性 id呢?或者說,對于任何類型,我如何知道要比較的是哪個屬性?答案就是:委托。通過委托,要比較什么屬性由外部指定。這也是linq 擴展方法的設計,參數都是委托類型的,也就是規則由外部定義,內部只負責調用。ok,我們看最后實現的代碼:

    //通過繼承EqualityComparer類也是一樣的。    class CustomerEqualityComparer<T,V> : IEqualityComparer<T>    {        PRivate IEqualityComparer<V> comparer;        private Func<T, V> selector;        public CustomerEqualityComparer(Func<T, V> selector)            :this(selector,EqualityComparer<V>.Default)        {                    }        public CustomerEqualityComparer(Func<T, V> selector, IEqualityComparer<V> comparer)        {            this.comparer = comparer;            this.selector = selector;        }        public bool Equals(T x, T y)        {            return this.comparer.Equals(this.selector(x), this.selector(y));        }        public int GetHashCode(T obj)        {            return this.comparer.GetHashCode(this.selector(obj));        }    }

 

(補充1)之前沒有把擴展方法貼出來,而且看到有朋友提到比較字符串忽略大小寫的問題(其實上面有兩個構造函數就可以解決這個問題)。這里擴展方法可以寫為:

    static class EnumerableExtention    {        public static IEnumerable<TSource> Distinct<TSource,TKey>(this IEnumerable<TSource> source, Func<TSource,TKey> selector)        {            return source.Distinct(new CustomerEqualityComparer<TSource,TKey>(selector));        }        //4.0以上最后一個參數可以寫成默認參數 EqualityComparer<T>.Default,兩個擴展Distinct可以合并為一個。        public static IEnumerable<TSource> Distinct<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> selector, IEqualityComparer<TKey> comparer)        {            return source.Distinct(new CustomerEqualityComparer<TSource, TKey>(selector,comparer));        }    }

例如,要根據Person的Name忽略大小寫比較,就可以寫成:

list.Distinct(x => x.Name,StringComparer.CurrentCultureIgnoreCase).ToList(); //StringComparer實現了IEqualityComaparer<string> 接口

 

二、通過哈希表。第一種做法的缺點是不僅要定義新的擴展方法,還要定義一個新類。能不能只有一個擴展方法就搞定?可以,通過Dictionary就可以搞定(有HashSet就用HashSet)。實現方式如下:

        public static IEnumerable<TSource> Distinct<TSource,TKey>(this IEnumerable<TSource> source, Func<TSource,TKey> selector)        {                        Dictionary<TKey, TSource> dic = new Dictionary<TKey, TSource>();            foreach (var s in source)            {                TKey key = selector(s);                if (!dic.ContainsKey(key))                    dic.Add(key, s);            }            return dic.Select(x => x.Value);        }

 

三、重寫object方法。能不能連擴展方法也不要了?可以。我們知道 object 是所有類型的基類,其中有兩個虛方法,Equals、GetHashCode,默認情況下,.net 就是通過這兩個方法進行對象間的比較的,那么linq 無參的Distinct 是不是也是根據這兩個方法來進行判斷的?我們在Person里 override 這兩個方法,并實現自己的比較規則。打上斷點調試,發現在執行Distinct時,是會進入到這兩個方法的。代碼如下:

class Person{    public int ID { get; set; }    public string Name { get; set; }    public override bool Equals(object obj)    {        Person p = obj as Person;        return this.ID.Equals(p.ID);    }    public override int GetHashCode()    {        return this.ID.GetHashCode();    }}

在我的需求里,是根據id去重的,所以第三種方式提供了最優雅的實現。如果是其它情況,用前面的方法更通用。


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 高台县| 临武县| 长海县| 新巴尔虎右旗| 金湖县| 固安县| 微博| 英山县| 库车县| 来凤县| 千阳县| 昌宁县| 德保县| 临沭县| 贺州市| 绍兴市| 鄂托克旗| 社会| 温泉县| 长葛市| 青河县| 巴彦县| 鸡东县| 新兴县| 庆安县| 上杭县| 四川省| 澄江县| 汕尾市| 焦作市| 敦化市| 古蔺县| 乐平市| 和田县| 丰城市| 临武县| 隆安县| 临武县| 衡阳县| 丹棱县| 微山县|