博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
转贴:Linq的Distinct太不给力了
阅读量:7044 次
发布时间:2019-06-28

本文共 3969 字,大约阅读时间需要 13 分钟。

假设我们有一个类: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 的结果是:

因为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());结果如下:

 

 

 

 

现在假设我们要 按照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"));

 

结果如下:

 

 

 

 

为什么微软不提供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();

转载于:https://www.cnblogs.com/qouoww/archive/2012/05/23/Linq.html

你可能感兴趣的文章
PowerShell检测并添加用户权限
查看>>
CCNP笔记——MST上
查看>>
php5中const、define和static
查看>>
HNUSTOJ-1695 跳格子(略感头疼)
查看>>
Python 代码规范
查看>>
DNS服务的配置与管理(2) DNS的理论知识
查看>>
2.Apache + Tomcat + mod_jk实现集群服务
查看>>
1.jeesite环境搭建
查看>>
Android实践项目汇报(四)
查看>>
destoon去掉会员注册email验证
查看>>
Python单元测试
查看>>
MySQL数据库的创建&删除&选择
查看>>
CSS 实践:实现下拉菜单的方法
查看>>
手机扫一扫车牌即可识别出结果的sdk
查看>>
初级程序员面试不靠谱指南(五)
查看>>
CF1109F Sasha and Algorithm of Silence's Sounds
查看>>
Python中的random模块
查看>>
oracle定时任务失效
查看>>
CString.Format的详细用法(转)
查看>>
AOP 与 Spring中AOP使用(下)
查看>>