手机
当前位置:查字典教程网 >编程开发 >C#教程 >为何Linq的Distinct实在是不给力
为何Linq的Distinct实在是不给力
摘要:假设我们有一个类:ProductpublicclassProduct{publicstringId{get;set;}publicstrin...

假设我们有一个类:Product

public class Product

{

public string Id { get; set; }

public string Name { get; set; }

}

Main函数如下:

static void Main()

{

List<Product> products = new List<Product>()

{

new Product(){ Id="1", Name="n1"},

new Product(){ Id="1", Name="n2"},

new Product(){ Id="2", Name="n1"},

new Product(){ Id="2", Name="n2"},

};

var distinctProduct = products.Distinct();

Console.ReadLine();

}

可以看到distinctProduct 的结果是:

为何Linq的Distinct实在是不给力1

因为Distinct 默认比较的是Product对象的引用,所以返回4条数据。

那么如果我们希望返回Id唯一的product,那么该如何做呢?

Distinct方法还有另一个重载:

//通过使用指定的 System.Collections.Generic.IEqualityComparer<T> 对值进行比较

//返回序列中的非重复元素。

public static IEnumerable<TSource> Distinct<TSource>(this IEnumerable<TSource> source,

IEqualityComparer<TSource> comparer);

该重载接收一个IEqualityComparer的参数。

假设要按Id来筛选,那么应该新建类ProductIdComparer 内容如下:

public class ProductIdComparer : IEqualityComparer<Product>

{

public bool Equals(Product x, Product y)

{

if (x == null)

return y == null;

return x.Id == y.Id;

}

public int GetHashCode(Product obj)

{

if (obj == null)

return 0;

return obj.Id.GetHashCode();

}

}

使用的时候,只需要

var distinctProduct = products.Distinct(new ProductIdComparer());

结果如下:

为何Linq的Distinct实在是不给力2

现在假设我们要 按照 Name来筛选重复呢?

很明显,需要再添加一个类ProductNameComparer.

那能不能使用泛型类呢??

新建类PropertyComparer<T> 继承IEqualityComparer<T> 内容如下:

public class PropertyComparer<T> : IEqualityComparer<T>

{

private PropertyInfo _PropertyInfo;

/// <summary>

/// 通过propertyName 获取PropertyInfo对象

/// </summary>

/// <param name="propertyName"></param>

public PropertyComparer(string propertyName)

{

_PropertyInfo = typeof(T).GetProperty(propertyName,

BindingFlags.GetProperty | BindingFlags.Instance | BindingFlags.Public);

if (_PropertyInfo == null)

{

throw new ArgumentException(string.Format("{0} is not a property of type {1}.",

propertyName, typeof(T)));

}

}

#region IEqualityComparer<T> Members

public bool Equals(T x, T y)

{

object xValue = _PropertyInfo.GetValue(x, null);

object yValue = _PropertyInfo.GetValue(y, null);

if (xValue == null)

return yValue == null;

return xValue.Equals(yValue);

}

public int GetHashCode(T obj)

{

object propertyValue = _PropertyInfo.GetValue(obj, null);

if (propertyValue == null)

return 0;

else

return propertyValue.GetHashCode();

}

#endregion

}

主要是重写的Equals 和GetHashCode 使用了属性的值比较。

使用的时候,只需要:

//var distinctProduct = products.Distinct(new PropertyComparer<Product>("Id"));

var distinctProduct = products.Distinct(new PropertyComparer<Product>("Name"));

结果如下:

为何Linq的Distinct实在是不给力3

为什么微软不提供PropertyEquality<T> 这个类呢?

按照上面的逻辑,这个类应该没有很复杂啊,细心的同学可以发现PropertyEquality 大量的使用了反射。每次获取属性的值的时候,都在调用

_PropertyInfo.GetValue(x, null);

可想而知,如果要筛选的记录非常多的话,那么性能无疑会受到影响。

为了提升性能,可以使用表达式树将反射调用改为委托调用,

具体代码如下:

public class FastPropertyComparer<T> : IEqualityComparer<T>

{

private Func<T, Object> getPropertyValueFunc = null;

/// <summary>

/// 通过propertyName 获取PropertyInfo对象

/// </summary>

/// <param name="propertyName"></param>

public FastPropertyComparer(string propertyName)

{

PropertyInfo _PropertyInfo = typeof(T).GetProperty(propertyName,

BindingFlags.GetProperty | BindingFlags.Instance | BindingFlags.Public);

if (_PropertyInfo == null)

{

throw new ArgumentException(string.Format("{0} is not a property of type {1}.",

propertyName, typeof(T)));

}

ParameterExpression expPara = Expression.Parameter(typeof(T), "obj");

MemberExpression me = Expression.Property(expPara, _PropertyInfo);

getPropertyValueFunc = Expression.Lambda<Func<T, object>>(me, expPara).Compile();

}

#region IEqualityComparer<T> Members

public bool Equals(T x, T y)

{

object xValue = getPropertyValueFunc(x);

object yValue = getPropertyValueFunc(y);

if (xValue == null)

return yValue == null;

return xValue.Equals(yValue);

}

public int GetHashCode(T obj)

{

object propertyValue = getPropertyValueFunc(obj);

if (propertyValue == null)

return 0;

else

return propertyValue.GetHashCode();

}

#endregion

}

可以看到现在获取值只需要getPropertyValueFunc(obj) 就可以了。

使用的时候:

var distinctProduct = products.Distinct(new FastPropertyComparer<Product>("Id")).ToList();

【为何Linq的Distinct实在是不给力】相关文章:

C#中的yield关键字的使用方法介绍

c#中Linq to Sql 增删除的实例

Win Form 的 Splitter 使用心得与技巧

C#中的where泛型约束介绍

C# Linq读取XML文件的实例

C#中实现任意List的全组合算法代码

.net(c#)中的new关键字详细介绍

深入解析c#中枚举类型的定义与使用

C#关机小程序源码

C#中fixed关键字的作用总结

精品推荐
分类导航