zoukankan      html  css  js  c++  java
  • C# 中distinct的使用

    假设我们有一个类:Product

    public class Product
    {
        public string Id { get; set; }
        public string Name { get; set; }
    }

    Main函数如下:

    static void Main()
    {
        List<Product> products = new List<Product>()
        {
            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 的结果是:

    image

    因为Distinct 默认比较的是Product对象的引用,所以返回4条数据。

    那么如果我们希望返回Id唯一的product,那么该如何做呢?

    Distinct方法还有另一个重载:

    //通过使用指定的 System.Collections.Generic.IEqualityComparer<T> 对值进行比较
    //返回序列中的非重复元素。
     public static IEnumerable<TSource> Distinct<TSource>(this IEnumerable<TSource> source, 
    IEqualityComparer<TSource> comparer);

    该重载接收一个IEqualityComparer的参数。

    假设要按Id来筛选,那么应该新建类ProductIdComparer 内容如下:

    public class ProductIdComparer : IEqualityComparer<Product>
    {
        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());

    结果如下:

    image

    现在假设我们要 按照 Name来筛选重复呢?

    很明显,需要再添加一个类ProductNameComparer.

    那能不能使用泛型类呢??

    新建类PropertyComparer<T> 继承IEqualityComparer<T> 内容如下:

    public class PropertyComparer<T> : IEqualityComparer<T>
    {
        private PropertyInfo _PropertyInfo;
    
        /// <summary>
        /// 通过propertyName 获取PropertyInfo对象    
        /// </summary>
        /// <param name="propertyName"></param>
        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<T> 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<Product>("Id"));
    var distinctProduct = products.Distinct(new PropertyComparer<Product>("Name"));

    结果如下:

    image

    为什么微软不提供PropertyEquality<T> 这个类呢?

    按照上面的逻辑,这个类应该没有很复杂啊,细心的同学可以发现PropertyEquality 大量的使用了反射。每次获取属性的值的时候,都在调用 
    _PropertyInfo.GetValue(x, null);

    可想而知,如果要筛选的记录非常多的话,那么性能无疑会受到影响。

    为了提升性能,可以使用表达式树将反射调用改为委托调用

    具体代码如下:

    public class FastPropertyComparer<T> : IEqualityComparer<T>
    {
        private Func<T, Object> getPropertyValueFunc = null;
    
        /// <summary>
        /// 通过propertyName 获取PropertyInfo对象
        /// </summary>
        /// <param name="propertyName"></param>
        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<Func<T, object>>(me, expPara).Compile();
        }
    
        #region IEqualityComparer<T> 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<Product>("Id")).ToList();


    http://www.cnblogs.com/joyang/p/5702472.html
  • 相关阅读:
    ul内的li横向排列左右滑动
    git使用中常见报错解决方法
    vue 中子组件用watch方法监听父组件传来的参数,用handler方法绑定,当父组件参数不改变时,handler方法不执行
    [Vue warn]: Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders.
    vue 动态更改classname
    react怎么添加运用自定义组件
    app项目中遇到TCP分包,H5端对分包进行拼包
    Error: Invalid CSS after "xxx": expected 1 selector or at-rule, was "{}"
    'touch' 不是内部或外部命令,也不是可运行的程序或批处理文件。
    微信小程序调用快递物流查询API的实现方法
  • 原文地址:https://www.cnblogs.com/cmblogs/p/7070313.html
Copyright © 2011-2022 走看看