LINQ Except"引用类型"用法

本文详细介绍了在使用Linq进行自定义对象数组操作时,如何通过实现IEqualityComparer接口来进行对象的比较,包括Distinct、Except、Intersect等方法的正确使用方式,并提供了具体的示例代码。

值类型的比较Except 直接就比了

引用类型的如下:

var resultExcept = Expert_ItemSource.Except(Invert_ItemSource, new MyCompare()).ToList();
 //比较器
public class MyCompare : IEqualityComparer<ConsultationExpertApiModel>
        {
            public bool Equals(ConsultationExpertApiModel b1, ConsultationExpertApiModel b2)
            {
                if (b1.MEMBER_ID == b2.MEMBER_ID)
                {
                    return true;
                }
                else
                {
                    return false;
                }
            }
            public int GetHashCode(ConsultationExpertApiModel bx)
            {
                int hCode = bx.MEMBER_ID.GetHashCode() ^ bx.MEMBER_ID.GetHashCode();
                return hCode.GetHashCode();
            }
        }
List<int> list1 = new List<int>();
list1.Add(1);
list1.Add(2);
list1.Add(3);
List<int> list2 = new List<int>();
list2.Add(3);
list2.Add(4);
list2.Add(5);
//得到的结果是4,5 即减去了相同的元素。
List<int> list3 = list2.Except(list1).ToList();
foreach (int i in list3)
{
    MessageBox.Show(i.ToString());
}

当我们用Linq操作我们自定义的对像数组时,我们会发现有些方法直接使用的话根本不起作用,比如:Distinct、Except、Intersect等扩展方法。

对于我们自定义的对象的比较,我们必须实现IEqualityComparer接口来判断两个对象的相等性。

示例代码如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace lambda
{
    class Program
    {
        static void Main(string[] args)
        {
            Park p1 = new Park { ticketPrice = 55, address = "南京", peoples = 85 };
            Park p2 = new Park { ticketPrice = 85, address = "北京", peoples = 75 };
            Park p3 = new Park { ticketPrice = 78, address = "多伦多", peoples = 100 };
            List<Park> parks = new List<Park>(){
                new Park { ticketPrice = 11, address = "天堂", peoples = 1000 },
                new Park { ticketPrice = 11, address = "天堂", peoples = 1000 }
            };
            parks.Add(p1);
            parks.Add(p2);
            parks.Add(p3);

            var diff = from c in parks.Distinct(new Park())

                       select c;
            foreach (var item in diff)
            {
                Console.WriteLine(item.address);
            }
        }

    }


    class Park : IEqualityComparer<Park>
    {
        public double ticketPrice { get; set; }
        public string address { get; set; }
        public int peoples { get; set; }

        public bool Equals(Park x, Park y)    //比较x和y对象是否相同,按照地址比较
        {
            return x.address == y.address;
        }

        public int GetHashCode(Park obj)  
        {
            return obj.ToString().GetHashCode();
        }
    }
}

下面的示例演示如何实现的相等比较器可在 Except<TSource> 方法。

public class Product
{
    public string Name { get; set; }
    public int Code { get; set; }
}


class ProductComparer : IEqualityComparer<Product>
{

    public bool Equals(Product x, Product y)
    {


        if (Object.ReferenceEquals(x, y)) return true;


        if (Object.ReferenceEquals(x, null) || Object.ReferenceEquals(y, null))
            return false;

        return x.Code == y.Code && x.Name == y.Name;
    }

    public int GetHashCode(Product product)
    { 
        if (Object.ReferenceEquals(product, null)) return 0;

        int hashProductName = product.Name == null ? 0 : product.Name.GetHashCode();

        int hashProductCode = product.Code.GetHashCode();

        return hashProductName ^ hashProductCode;
    }

}

实现此比较器后,您可以使用的序列 Product 中的对象 Except<TSource> 方法,如下面的示例中所示。

Product[] fruits1 = { new Product { Name = "apple", Code = 9 }, 
                       new Product { Name = "orange", Code = 4 },
                        new Product { Name = "lemon", Code = 12 } };

Product[] fruits2 = { new Product { Name = "apple", Code = 9 } };

IEnumerable<Product> except =
    fruits1.Except(fruits2, new ProductComparer());

foreach (var product in except)
    Console.WriteLine(product.Name + " " + product.Code);

/* 
  orange 4
  lemon 12
*/

  1. GetHashCode是判断对象是否相等的快速检查,所以GetHashCode不需要太复杂,因为即使GetHashCode判断相同了,后面还有Equals保证精确对比。(Note:但是尽量保证GetHashCode就能判断出来,太多的hash冲突,也会增加不必要的开销。这就要看GetHashCode重写得有没有水平了)
  2. 理解重写GetHashCode的必要性。

建议题主了解一下哈希和哈希表,就会知道这个哈希码(HashCode)是什么东西了。

简而言之,哈希代码是一个用于在相等测试过程中标识对象的数值。它还可以作为一个集合中的对象的索引。如果两个对象的 Equals 比较结果相等,则每个对象的 GetHashCode 方法都必须返回同一个值。 如果两个对象的比较结果不相等,这两个对象的 GetHashCode 方法不一定返回不同的值。

简而言之,如果你发现两个对象 GetHashCode() 的返回值相等,那么这两个对象就很可能是同一个对象;但如果返回值不相等,这两个对象一定不是同一个对象。


实例

OrderCompare oc= new OrderCompare();
IEnumerable<Model_OrderInfo> DifferenceSequence = orderList1.Except(OrderinfoList2,oc);

if (DifferenceSequence.Any()) 
{
    foreach (Model_OrderInfo item in DifferenceSequence) 
    {
        sBuilder.Append(item.OrderSn + "--" + item.ProdSn + "<br/>");
    }
    new SendMailHelper().SendMail("123@qq.com", "邮件", null, "不一致的数据",
                                            sBuilder.ToString());
} else 
{
    LogHelper.WriteFile("正确!---------" + DateTime.Now);
}
   //比较器
        public class OrderCompare : IEqualityComparer<Model_OrderInfo>
        {
            public bool Equals(Model_OrderInfo o1, Model_OrderInfo o2)
            {

                if (Object.ReferenceEquals(o1, o2))
                {
                    return true;
                }

                if (Object.ReferenceEquals(o1, null) || Object.ReferenceEquals(o2, null))
                {
                    return false;
                }

                bool flag = o1.Name.Equals(o2.Name) && o1.Age.Equals(o2.Age) && o1.Mobile.Equals(o2.Mobile) && o1.Home.Equals(o2.Home) && o1.Gender.Equals(o2.Gender);
                return flag;
            }

            public int GetHashCode(Model_OrderInfo oinfo)
            { 
                if (oinfo == null)
                {
                    return 0;
                }
                else
                {
                    int hashresult = oinfo.ToString().GetHashCode(); 
                    return hashresult;

                } 
            } 
        }
<think>我们被要求提供关于LINQ的详细介绍,包括全部用法和使用指南。根据引用内容,LINQ(Language Integrated Query)是.NET框架中的一项技术,它允许在代码中使用类似SQL的查询语法来查询各种数据源。我们将从基础开始,逐步介绍LINQ的关键概念和用法。 ### 1. LINQ基础 LINQ提供了一套统一的编程模型,用于查询和操作数据,无论数据来自内存对象(LINQ to Objects)、数据库(LINQ to SQL)、XML(LINQ to XML)还是其他数据源。LINQ查询操作由三个基本操作组成: - **获取数据源**:指定要查询的数据源。 - **创建查询**:定义查询表达式。 - **执行查询**:执行查询以获取结果。 ### 2. LINQ查询语法 LINQ有两种形式的语法:**查询表达式语法**(类似于SQL)和**方法语法**(使用扩展方法和Lambda表达式)。查询表达式在编译时会转换为方法语法。 #### 2.1 查询表达式语法 一个基本的查询表达式包含`from`、`where`、`select`等子句。例如: ```csharp // 数据源 int[] numbers = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 }; // 查询表达式:查询所有小于5的数 var lowNums = from num in numbers where num < 5 select num; ``` #### 2.2 方法语法 同样的查询使用方法语法: ```csharp var lowNums = numbers.Where(num => num < 5); ``` ### 3. 常用查询子句 根据引用[1]和[3],以下是LINQ中常用的关键字及其说明: - **from**:指定范围变量和数据源。每个查询表达式必须以`from`子句开头。 - **where**:过滤条件,基于布尔表达式筛选元素。 - **select**:指定查询结果的形式(投影)。 - **group**:按照指定的键分组结果。 - **orderby**:排序(可指定升序`ascending`或降序`descending`)。 - **join**:连接两个数据源(类似于SQL中的JOIN)。 - **let**:引入临时变量以存储子表达式的结果。 - **into**:用于`group`或`join`后,作为对结果的引用。 ### 4. 分组(group) 分组使用`group by`子句,将数据按指定键分组。例如: ```csharp var query = from student in students group student by student.LastName[0] into g orderby g.Key select g; ``` ### 5. 连接(join) 连接操作允许将两个数据源中的元素关联起来。例如,内连接: ```csharp var query = from category in categories join product in products on category.ID equals product.CategoryID select new { ProductName = product.Name, Category = category.Name }; ``` ### 6. 排序(orderby) 可以按一个或多个键排序: ```csharp var sortedStudents = from student in students orderby student.LastName descending, student.FirstName select student; ``` ### 7. 分页 引用[2]展示了使用`Skip`和`Take`进行分页: ```csharp var bulletins = from b in dataContext.Bulletins where b.Category == 3 orderby b.ID descending select b; return bulletins.Skip(pageSize * (pageNumber - 1)).Take(pageSize).ToList(); ``` ### 8. 插入操作(Insert) 引用[2]提到,使用LINQ to SQL插入数据: ```csharp // 创建新对象 Product newProduct = new Product { Name = "New Product", Price = 9.99 }; // 添加到数据上下文 dataContext.Products.InsertOnSubmit(newProduct); // 提交更改 dataContext.SubmitChanges(); ``` ### 9. 延迟执行与立即执行 LINQ查询分为延迟执行和立即执行: - **延迟执行**:查询定义后不会立即执行,而是在枚举结果(如使用`foreach`)时执行。 - **立即执行**:使用聚合函数(如`Count`、`ToList`、`ToArray`)会立即执行查询。 ### 10. LINQ提供程序 - **LINQ to Objects**:查询内存中的集合。 - **LINQ to SQL**:查询关系数据库(如SQL Server),通过对象关系映射(ORM)。 - **LINQ to Entities**:使用Entity Framework查询数据库。 - **LINQ to XML**:查询XML文档。 ### 11. 使用Lambda表达式 方法语法中大量使用Lambda表达式。例如: ```csharp var highNums = numbers.Where(n => n > 5).OrderByDescending(n => n); ``` ### 12. 投影(Select) `select`子句可以转换结果: ```csharp var names = from student in students select student.FirstName + " " + student.LastName; ``` ### 13. 聚合函数 LINQ提供了一系列聚合函数,如`Count`、`Sum`、`Average`、`Min`、`Max`: ```csharp int count = numbers.Count(); int evenCount = numbers.Count(n => n % 2 == 0); double average = numbers.Average(); ``` ### 14. 其他操作 - **Distinct**:去重。 - **Concat**:连接两个序列。 - **Union**:并集(去重)。 - **Intersect**:交集。 - **Except**:差集。 ### 15. 注意事项 - 使用LINQ to SQL或Entity Framework时,查询会转换为SQL语句,因此要注意转换是否成功。 - 延迟执行可能导致多次访问数据库,应适时使用`ToList()`等。 ### 16. 示例汇总 以下是一个综合示例: ```csharp // 数据源 List<Student> students = GetStudents(); // 查询:成绩大于80分的学生,按姓名排序,然后按成绩降序 var query = from s in students where s.Score > 80 orderby s.Name, s.Score descending select new { s.Name, s.Score }; // 方法语法 var methodQuery = students.Where(s => s.Score > 80) .OrderBy(s => s.Name) .ThenByDescending(s => s.Score) .Select(s => new { s.Name, s.Score }); ``` 以上是LINQ的详细介绍和使用指南。LINQ功能强大,可以极大地提高数据操作的效率和代码的可读性。 §§相关问题§§ 1. LINQ查询中如何实现多表连接? 2. LINQ to SQL和Entity Framework有什么区别? 3. 如何优化LINQ查询的性能? 4. LINQ中如何处理空值(NULL)? 5. LINQ支持哪些类型的数据库?</think>### LINQ 详细介绍与使用指南 #### 一、LINQ 核心概念 LINQ(Language Integrated Query)是 .NET 中的语言集成查询技术,允许使用类似 SQL 的语法直接操作**对象、数据库、XML 等数据源**。其核心优势在于: 1. **统一查询模型**:对内存对象、数据库、XML 等使用相同语法 2. **编译时类型检查**:避免运行时错误 3. **智能提示支持**:IDE 自动补全 #### 二、基础语法结构 ```csharp from 范围变量 in 数据源 [where 条件] [orderby 字段 [ascending|descending]] [select 投影] [group 分组字段 by 键] ``` #### 三、核心子句详解 1. **`from`**:指定数据源和范围变量 ```csharp from product in products // products 是集合 ``` 2. **`where`**:过滤条件(支持逻辑运算符) ```csharp where product.Price > 100 && product.Stock > 0 ``` 3. **`select`**:结果投影(支持匿名类型) ```csharp select new { product.Name, product.Price } // 创建新对象 ``` 4. **`orderby`**:排序(多字段用逗号分隔) ```csharp orderby product.Category ascending, product.Price descending ``` 5. **`group`**:分组查询 ```csharp group product by product.Category into g // g 是分组结果 select new { Category = g.Key, Count = g.Count() } ``` 6. **`join`**:多表连接(需指定关联键) ```csharp from p in products join c in categories on p.CategoryID equals c.ID select new { p.Name, Category = c.Name } ``` #### 四、方法语法 vs 查询语法 | **场景** | 查询语法 | 方法语法(Lambda) | |------------------|--------------------------|--------------------------| | 基本过滤 | `where product.Price > 100` | `products.Where(p => p.Price > 100)` | | 排序 | `orderby product.Name` | `products.OrderBy(p => p.Name)` | | 分页 | 需结合方法语法 | `products.Skip(20).Take(10)` | | 聚合计算 | 需结合方法语法 | `products.Average(p => p.Price)` | #### 五、数据操作指南 1. **查询数据库(LINQ to SQL)** ```csharp var db = new DataContext(); // 数据上下文 var query = from u in db.Users where u.Age > 18 select u; ``` 2. **插入数据** ```csharp var newUser = new User { Name = "Alice", Age = 25 }; db.Users.InsertOnSubmit(newUser); db.SubmitChanges(); // 提交到数据库[^2] ``` 3. **更新数据** ```csharp var user = db.Users.First(u => u.Id == 1); user.Age = 30; db.SubmitChanges(); ``` 4. **分页实现** ```csharp var pageData = products.OrderBy(p => p.Id) .Skip((pageIndex - 1) * pageSize) .Take(pageSize) .ToList(); // [^2] ``` #### 六、高级功能 1. **延迟执行(Deferred Execution)** ```csharp var query = products.Where(p => p.Price > 100); // 未立即执行 var result = query.ToList(); // 执行查询 ``` 2. **即时执行(Immediate Execution)** ```csharp var count = products.Count(); // 立即执行 ``` 3. **`let` 子句**:创建临时变量 ```csharp from p in products let discount = p.Price * 0.9 // 临时变量 where discount > 50 select discount ``` 4. **聚合函数** ```csharp var stats = new { Total = products.Sum(p => p.Price), Avg = products.Average(p => p.Price), Max = products.Max(p => p.Price) }; ``` #### 七、最佳实践 1. **性能优化** - 避免 `N+1 查询`:使用 `Join` 代替循环查询 - 仅选择必要字段:`select new { ... }` 减少数据传输 - 分批次处理大数据:`Take`/`Skip` 分页 2. **异常处理** ```csharp try { db.SubmitChanges(); } catch (ChangeConflictException) { db.ChangeConflicts.ResolveAll(RefreshMode.OverwriteCurrentValues); } ``` #### 八、典型应用场景 1. 数据库 CRUD 操作 2. XML 文档查询(LINQ to XML) 3. 内存集合过滤/转换 4. 实体关系导航(如 `customer.Orders`) > 提示:LINQ 查询在编译时转换为表达式树,由提供程序(如 Entity Framework)生成优化后的 SQL[^1]。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值