linqin,Linq的Distinct太不给力了

假设我们有一个类:Product
public class Product { public string Id { get; set; } public string Name { get; set; } }
Main函数如下:
static void Main() { List products = new List() { 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 的结果是:
imageLinq的Distinct太不给力了linqin
 
因为Distinct 默认比较的是Product对象的引用,所以返回4条数据。
那么如果我们希望返回Id唯一的product,那么该如何做呢?
 

Distinct方法还有另一个重载:
//通过使用指定的 System.Collections.Generic.IEqualityComparer 对值进行比较 //返回序列中的非重复元素。 public static IEnumerable Distinct(this IEnumerable source,
IEqualityComparer comparer);
该重载接收一个IEqualityComparer的参数。
假设要按Id来筛选,那么应该新建类ProductIdComparer 内容如下:
public class ProductIdComparer : IEqualityComparer { 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());
结果如下:
imageimageLinq的Distinct太不给力了linqin
 
现在假设我们要 按照 Name来筛选重复呢?
很明显,需要再添加一个类ProductNameComparer.
那能不能使用泛型类呢??

新建类PropertyComparer 继承IEqualityComparer 内容如下:
public class PropertyComparer : IEqualityComparer { private PropertyInfo _PropertyInfo; /// /// 通过propertyName 获取PropertyInfo对象
///
///
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 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("Id")); var distinctProduct = products.Distinct(new PropertyComparer("Name"));
 
结果如下:
imageimageimageLinq的Distinct太不给力了linqin
 
为什么微软不提供PropertyEquality 这个类呢?
按照上面的逻辑,这个类应该没有很复杂啊,细心的同学可以发现PropertyEquality 大量的使用了反射。每次获取属性的值的时候,都在调用 _PropertyInfo.GetValue(x, null);
可想而知,如果要筛选的记录非常多的话,那么性能无疑会受到影响。
为了提升性能,可以使用表达式树将反射调用改为委托调用
具体代码如下:
 
public class FastPropertyComparer : IEqualityComparer { private Func getPropertyValueFunc = null; /// /// 通过propertyName 获取PropertyInfo对象 /// ///
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>(me, expPara).Compile(); } #region IEqualityComparer 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("Id")).ToList();
Tags:  linqin

延伸阅读

最新评论

发表评论