`
sty2008boy
  • 浏览: 301748 次
  • 性别: Icon_minigender_1
  • 来自: 太原
社区版块
存档分类
最新评论

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();

分享到:
评论

相关推荐

    用于Linq Distinct()的通用IEqualityComparer

    在.NET框架中,`LINQ (Language Integrated Query)` 提供了一种优雅的方式来处理数据查询,其中`Distinct()`方法用于去除序列中的重复项。然而,在某些情况下,标准的`Distinct()`可能无法满足我们的需求,因为它...

    c#Linq distinct不会调用Equals方法详解

    我有以下课程 public class ModInfo : IEquatable { public int ID { get; set; } public string MD5 { get; set; } public bool Equals(ModInfo other) ... return other.MD5.Equals(MD5);...public void Relo

    利用linq给两个datatable去重

    两个datatable,t1和t2 t2是t1的子集,用linq去掉t1中包含t2的数据

    为何Linq的Distinct实在是不给力

    在.NET编程中,LINQ(Language Integrated Query,语言集成查询)提供了一种简洁的方式来处理集合数据,其中`Distinct`方法用于去除序列中的重复元素。然而,在某些情况下,`Distinct`的默认行为可能并不符合我们的...

    利用Distinct()内置方法对List集合的去重问题详解

    说到对集合去重处理,第一时间想到的肯定是Linq的Distinct扩展方式,对于一般的值类型集合去重,很好处理,直接list.Distinct()即可。但是如果想要对一个引用类型的集合去重(属性值都相同就认为重复),就会发现,...

    LINQ to SQL手册

    **LINQ to SQL** 是 .NET Framework 中的一个技术,它允许开发者使用 C# 或 VB.NET 语言的查询表达式语法来操作数据库。本手册详细介绍了使用 LINQ to SQL 进行数据查询、操作和更新的各种方法,涵盖了从基础到高级...

    C# Distinct和重写IEqualityComparer时要知道的二三事

    我们在想对一个可枚举的对象集合进行去重操作时,一般第一个想到的就是就是Linq的Distinct方法。 先定义一个类,然后使用Distinct方法去重 class Man { public int Age { get; set; } public string Name { get;...

    LINQ 实战 7/11

    4.4.3 Distinct操作符 107 4.4.4 转换操作符 108 4.4.5 聚合操作符 109 4.5 用不同的方式显示内存中的集合 110 4.5.1 排序 110 4.5.2 嵌套查询 111 4.5.3 分组 113 4.5.4 使用连接 114 4.5.5 分区...

    LINQ in Action PDF

    - **Distinct**: 去除序列中的重复元素。 - **Union/Intersect**: 合并两个序列,去除重复元素,或找出两个序列的交集。 **5. LINQ的异步查询** - **Async/Await**: .NET 4.0引入的新特性,允许异步执行LINQ查询,...

    精通LINQ程序设计

    LINQ提供了丰富的查询运营商,如`Select`、`Where`、`OrderBy`、`GroupBy`、`Join`和`Distinct`等,这些运营商允许开发者根据需求对数据进行过滤、排序、分组和转换。例如,`Where`用于筛选数据,`Select`用于投影...

    linq详细案例.

    本篇文章将详细讲解LINQ的相关知识,主要关注LINQ to SQL的使用,包括Where、Select和Distinct等操作。 **Where操作** `Where`是LINQ中最基础的查询操作,用于根据指定条件过滤数据。它有三种形式: 1. **简单...

    讲述ASP.NET中LINQ中文教程

    ### ASP.NET中LINQ中文教程知识点详解 #### 一、LINQ简介 - **定义**:LINQ(Language Integrated Query)是一种集成在.NET Framework 3.5中的技术,旨在为.NET开发者提供统一的数据查询和处理方式。LINQ使得开发者...

    linq入门到精通

    - `Distinct`:去除重复项。 - `Take` 和 `Skip`:用于截取查询结果的前N个元素或跳过前N个元素。 **4. LINQ的扩展方法** - 扩展方法是LINQ的重要特性,它们定义在静态类中,可以被添加到任何类型的实例上,如`...

    LINQ101个例子

    集运算符处理的是元素的唯一性,如`Union`合并两个序列并删除重复项,`Intersect`返回两个序列的交集,`Except`找出第一个序列中不在第二个序列的元素,而`Distinct`则用于去除序列中的重复元素。 6. **LINQ - ...

    LINQ中文教程.doc

    ### LINQ中文教程知识点梳理 #### 一、LINQ简介 - **定义**: LINQ (Language Integrated Query) 是一种在.NET Framework 3.5中引入的新特性,它允许开发者使用类似SQL的语法来查询和处理数据集合。LINQ旨在简化不同...

    LiNQ语句精典实例

    LiNQ 语句精典实例 LiNQ(Language Integrated Query)是一种强大的查询语言,允许开发者使用 SQL 语句风格的语法来查询和操作数据。下面是一些 LiNQ 语句的精典实例,展示了 LiNQ 的强大功能和灵活性。 基本语法 ...

    LINQ中文教程.pdf (比较适合入门)

    LINQ提供了`.Distinct()`方法来去除查询结果中的重复项,以及`group by`语法来进行数据分组,便于进行更深入的数据分析和汇总。例如,教程中提到了两种去重方法: 1. 使用`.Distinct()`方法: ```csharp var ...

    MSDN:101 LINQ Samples

    6. **LINQ - Set Operators**:集操作符,如Union、Intersect、Except和Distinct,用于处理不重复元素,实现集合的合并、交集、差集和去重。 7. **LINQ - Restriction Operators**:限制操作符,如Where、Take、...

    Linq与扩展方法

    除了上述功能,Linq还支持分组(`GroupBy`)、元素去重(`Distinct`)、多态查询(`OfType`)、数据转换(`Select`)等。`GroupBy`可以按照一个或多个键将数据分组,如按地区分组用户: ```csharp var usersByRegion...

Global site tag (gtag.js) - Google Analytics