为Distinct准备的通用对比器
使用Linq过滤重复对象的时候,我们使用Distinct。
但是Distinct对int long等值类型才有效果,对于对象我们需要自己写个对象。
以下利用泛型封装了两个类:
CommonComparer<T>
public class CommonComparer<T> : IEqualityComparer<T> { Func<T, string> GetStrigPropertyValueFunc; Func<T, int> GetInt32PropertyValueFunc; Func<T, long> GetInt64PropertyValueFunc; public CommonComparer(string propertyName) { var tType = typeof(T); PropertyInfo propertyInfo = tType.GetProperty(propertyName); ParameterExpression pExpress = Expression.Parameter(tType); MemberExpression bodyExpress = Expression.Property(pExpress, propertyInfo); switch (propertyInfo.PropertyType.Name) { case "Int32": GetInt32PropertyValueFunc = Expression.Lambda<Func<T, int>>(bodyExpress, pExpress).Compile(); break; case "Int64": GetInt64PropertyValueFunc = Expression.Lambda<Func<T, long>>(bodyExpress, pExpress).Compile(); break; case "String": GetStrigPropertyValueFunc = Expression.Lambda<Func<T, string>>(bodyExpress, pExpress).Compile(); break; default: throw new NotSupportedException("对比器只支持int32、int64、String"); } } public bool Equals(T x, T y) { if (GetStrigPropertyValueFunc != null) { var xValue = GetStrigPropertyValueFunc(x); var yValue = GetStrigPropertyValueFunc(y); if (xValue == null) return yValue == null; return xValue.Equals(yValue); } else if (GetInt32PropertyValueFunc != null) { var xValue = GetInt32PropertyValueFunc(x); var yValue = GetInt32PropertyValueFunc(y); if (xValue == null) return yValue == null; return xValue.Equals(yValue); } else if (GetInt64PropertyValueFunc != null) { var xValue = GetInt64PropertyValueFunc(x); var yValue = GetInt64PropertyValueFunc(y); if (xValue == null) return yValue == null; return xValue.Equals(yValue); } throw new NotSupportedException("没找到支持的委托类型"); } public int GetHashCode(T obj) { if (GetStrigPropertyValueFunc != null) { var value = GetStrigPropertyValueFunc(obj); if (obj == null) return 0; return value.GetHashCode(); } else if (GetInt32PropertyValueFunc != null) { var value = GetInt32PropertyValueFunc(obj); if (obj == null) return 0; return value.GetHashCode(); } else if (GetInt64PropertyValueFunc != null) { var value = GetInt64PropertyValueFunc(obj); if (obj == null) return 0; return value.GetHashCode(); } return 0; } }
ReflectCommonComparer<T>
public class ReflectCommonComparer<T> : IEqualityComparer<T> { string PropertyName; public ReflectCommonComparer(string propertyName) { PropertyName = propertyName; } object GetPropertyValue(T x) { PropertyInfo propertyInfo = typeof(T).GetProperty(PropertyName); return propertyInfo.GetValue(x); } public bool Equals(T x, T y) { var xValue = GetPropertyValue(x); var yValue = GetPropertyValue(y); if (xValue == null) return yValue == null; return xValue.Equals(yValue); } public int GetHashCode(T obj) { var value = GetPropertyValue(obj); if (obj == null) return 0; return value.GetHashCode(); } }
CommonComparer利用的是表达式树来实现的,ReflectCommonComparer是利用反射来实现的。网络上说利用的是表达式树来实现比反射更快。
我做了简单的时间测试,以下是截图:
1000次循环对比,反射更快呀
十万次对比,也是反射的比较快呀。
有可能是我写的表达式树有问题。 有空再去试试。
---
我把去重的数据量变多了之后的对比:
十万次,表达式树更快了。
我的结论是,去重的数据量多的话就用表达式树,少的话就用反射。大概超过80就需要用表达式树了。
因此以上还可以进一步分装。
public static class LinqExtension { public static IEnumerable<T> Distinct<T>(this IEnumerable<T> source, string propertyName) { if (source.Count() > 80) return source.Distinct(new CommonComparer<T>(propertyName)); else return source.Distinct(new ReflectCommonComparer<T>(propertyName)); } }
使用
var newList = list.Distinct("id").ToList(); // id是要用来判断去重的唯一值