• 转贴:Linq的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();

  • 相关阅读:
    sql 作业创建
    sqlserver的触发器练习实例
    js按钮频繁提交解决方案:
    C# 使用WebClient下载文件到本地目录
    放松读物,再看css禅意花园-总结HTML-又收到两个标记
    放松读物,再看css禅意花园-总结HTML
    放松读物,再看css禅意花园-HTML是CSS设计的基础
    线段的垂直平分线12
    放松读物,再看css禅意花园-第一章学到的教训-文本的可缩放
    放松读物,再看css禅意花园-第一章继续学到的教训
  • 原文地址:https://www.cnblogs.com/qouoww/p/Linq.html
Copyright © 2020-2023  润新知