前言
问题描述:Person类有两个属性ID(int)、Name(string)属性。筛选序列中不重复的Person。
1: public class Person
2: {
3: public int ID { get; set; }
4: public string Name { get; set; }
5: }
好吧,看样子得用Distinct方法。不过Distinct方法有两个重载。
1: public static IEnumerable<TSource> Distinct<TSource>(
2: this IEnumerable<TSource> source
3: )
4:
5: public static IEnumerable<TSource> Distinct<TSource>(
6: this IEnumerable<TSource> source,
7: IEqualityComparer<TSource> comparer
8: )
看样子已经够用了。不过如果我想只按ID唯一,或者按Name唯一呢?派生IEqualityComparer倒是可以解决。不过那也忒坑爹了。还是自己试着写一个扩展吧。
Distinct扩展
版本1
参考封装的Distinct的方法先写一个。声明一个List<TSource>,循环的时候比较,如果不同就加进去。
1: public static IEnumerable<TSource> Distinct<TSource, TProperty>(this IEnumerable<TSource> source, Func<TSource, TProperty> selector)
2: {
3: if (source == null) throw new ArgumentNullException("source");
4: if (selector == null) throw new ArgumentNullException("selector");
5: List<TSource> temp = new List<TSource>();
6: foreach (var item in source)
7: {
8: if (temp.Any(t => selector(t).Equals(selector(item))))
9: {
10: continue;
11: }
12: temp.Add(item);
13: }
14: return temp;
15: }
测试一下,没什么问题。不过在函数里声明List<TSource>感觉怪怪的,想办法换种方式。
1: List<Person> list = new List<Person>()
2: {
3: new Person() { ID = 1, Name = "Alen1" },
4: new Person() { ID = 1, Name = "Alen2" },
5: new Person() { ID = 2, Name = "Alen2" },
6: new Person() { ID = 2, Name = "Alen1" }
7: };
8:
9: var temp = list.Distinct(t => t.ID);
版本2
取指定属性的唯一序列,在循环的时候使用yield返回。
1: public static IEnumerable<TSource> Distinct<TSource, TProperty>(this IEnumerable<TSource> source, Func<TSource, TProperty> selector)
2: {
3: if (source == null) throw new ArgumentNullException("source");
4: if (selector == null) throw new ArgumentNullException("selector");
5:
6: var pdis = source.Select(t => selector(t)).Distinct();
7: foreach (var item in pdis)
8: {
9: yield return source.First(t => selector(t).Equals(item));
10: }
11: }
测试通过。
用Stopwatch分别测试两个函数循环不同次数的时间。对比一下,循环次数在10,000次以上时,版本2耗时大概是版本1的1/40,性能差距算是比较大。
后记
目前来说已经够用了,如果还有其他需求,再根据情况扩展吧。
1: list.Distinct(t => t.ID);
2: list.Distinct(t => new { t.ID, t.Name }); //根据ID及Name的值分别比较来取唯一,并非比较引用