为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是要用来判断去重的唯一值

  

时间: 2024-08-09 12:33:17

为Distinct准备的通用对比器的相关文章

Scut 的通用配置管理器

1. 配置节 ConfigSection private List<ConfigNode> _configNodes; public class ConfigNode { public ConfigNode() { } public ConfigNode(string key, string value) { Key = key; Value = value; } public string Key { get; set; } public string Value { get; set; }

数据处理分析对比器(至今为止最难的数据处理)上

实现json数据读取,选择任意数据个数  对选择数据的属性值进行分类和比对,这里存在一个二维表格生成问题,横向纵向都是变量. 今天先把数据读取的首页分析一下,要求 显示指定倒数任意条数据: json格式,(太长只贴了一个): [ { "cloudosclient_version": "1.1.2.18", "hardware_version": 1, "aaa":1111, "case_list": [ {

python 通用 修饰器

import functools def log(option): def dec(func): def swapper(*arg, **karg): functools.update_wrapper(swapper, func) opt = format(option,"^7") print '['+opt+"] enter",func.__name__ res = func(*arg, **karg) print '['+opt+'] exit',func.__

RBF神经网络学习算法及与多层感知器的比较

对于RBF神经网络的原理已经在我的博文<机器学习之径向基神经网络(RBF NN)>中介绍过,这里不再重复.今天要介绍的是常用的RBF神经网络学习算法及RBF神经网络与多层感知器网络的对比. 一.RBF神经网络学习算法 广义的RBF神经网络结构如下图所示: N-M-L结构对应着N维输入,M个数据中心点centers,L个输出. RBF 网络常用学习算法 RBF 网络的设计包括结构设计和参数设计.结构设计主要解决如何确定网络隐节点数的问题.参数设计一般需考虑包括3种参数:各基函数的数据中心和扩展常

Linux进程调度器的设计--Linux进程的管理与调度(十七)

日期 内核版本 架构 作者 GitHub CSDN 2016-06-14 Linux-4.6 X86 & arm gatieme LinuxDeviceDrivers Linux进程管理与调度 前景回顾 进程调度 内存中保存了对每个进程的唯一描述, 并通过若干结构与其他进程连接起来. 调度器面对的情形就是这样, 其任务是在程序之间共享CPU时间, 创造并行执行的错觉, 该任务分为两个不同的部分, 其中一个涉及调度策略, 另外一个涉及上下文切换. 内核必须提供一种方法, 在各个进程之间尽可能公平地

Linux进程调度器概述--Linux进程的管理与调度(十五)

日期 内核版本 架构 作者 GitHub CSDN 2016-06-14 Linux-4.6 X86 & arm gatieme LinuxDeviceDrivers Linux进程管理与调度 内存中保存了对每个进程的唯一描述, 并通过若干结构与其他进程连接起来. 调度器面对的情形就是这样, 其任务是在程序之间共享CPU时间, 创造并行执行的错觉, 该任务分为两个不同的部分, 其中一个涉及调度策略, 另外一个涉及上下文切换. 什么是调度器 通常来说,操作系统是应用程序和可用资源之间的媒介. 典型

Linux进程核心调度器之主调度器--Linux进程的管理与调度(十九)

日期 内核版本 架构 作者 GitHub CSDN 2016-06-30 Linux-4.6 X86 & arm gatieme LinuxDeviceDrivers Linux进程管理与调度 我们前面提到linux有两种方法激活调度器:核心调度器和 一种是直接的, 比如进程打算睡眠或出于其他原因放弃CPU 另一种是通过周期性的机制, 以固定的频率运行, 不时的检测是否有必要 因而内核提供了两个调度器主调度器,周期性调度器,分别实现如上工作, 两者合在一起就组成了核心调度器(core sched

Linux核心调度器之周期性调度器scheduler_tick--Linux进程的管理与调度(十八)

日期 内核版本 架构 作者 GitHub CSDN 2016-6-29 Linux-4.6 X86 & arm gatieme LinuxDeviceDrivers Linux进程管理与调度 我们前面提到linux有两种方法激活调度器:核心调度器和 一种是直接的, 比如进程打算睡眠或出于其他原因放弃CPU 另一种是通过周期性的机制, 以固定的频率运行, 不时的检测是否有必要 因而内核提供了两个调度器主调度器,周期性调度器,分别实现如上工作, 两者合在一起就组成了核心调度器(core schedu

采集器Server

序 公司搞工业的,面对不同的工业通讯数据协议,需要一个采集器(like ge.Collector)收集不同协议的数据,并将之数据使用指定格式上传到数据接收服务器,且本地做http发布服务. 设计 不同的工业采集2次开发模块可能会不同,有的是事件通知,有的是需要自己去要:因此需要一种通用的模式 —— 时序扫描模式.为了快速查找,因此我们从Dictionary派生一个CollectService,定义如下: 类似游戏中tick一样,通过容器式的自顶向下的扫描,嗯可以看到sender在这里,所有的数据