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
  • 相关阅读:
    UVA 408 (13.07.28)
    linux概念之用户,组及权限
    Java实现 蓝桥杯 历届试题 网络寻路
    Java实现 蓝桥杯 历届试题 约数倍数选卡片
    Java实现 蓝桥杯 历届试题 约数倍数选卡片
    Java实现 蓝桥杯 历届试题 约数倍数选卡片
    Java实现 蓝桥杯 历届试题 约数倍数选卡片
    Java实现 蓝桥杯 历届试题 约数倍数选卡片
    Java实现 蓝桥杯 历届试题 九宫重排
    Java实现 蓝桥杯 历届试题 九宫重排
  • 原文地址:https://www.cnblogs.com/sjqq/p/7185025.html
Copyright © 2011-2022 走看看