为Distinct准备的通用对比器

Posted 狂乱贵公子

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了为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是要用来判断去重的唯一值

  

以上是关于为Distinct准备的通用对比器的主要内容,如果未能解决你的问题,请参考以下文章

Greenplum 数据库 - Count Distinct Queries 缓慢的问题

Eclipse 中的通用代码片段或模板

Group by 和distinct对比

GLSL实现图像处理

OpenGL 之 Compute Shader(通用计算并行加速)

为啥保守光栅化无法为某些三角形调用片段着色器?