为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是利用反射来实现的。网络上说利用的是表达式树来实现比反射更快。

我做了简单的时间测试,以下是截图:

为Distinct准备的通用对比器

1000次循环对比,反射更快呀

为Distinct准备的通用对比器

十万次对比,也是反射的比较快呀。

有可能是我写的表达式树有问题。 有空再去试试。

---

我把去重的数据量变多了之后的对比:

为Distinct准备的通用对比器

十万次,表达式树更快了。

我的结论是,去重的数据量多的话就用表达式树,少的话就用反射。大概超过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是要用来判断去重的唯一值

  

上一篇:Python: Windows下pip安装库出错:Microsoft Visual C++ 9.0 is required < Unable to find vcvarsall.bat


下一篇:windows下搭建eclipse关于python的开发环境及初始化参数配置