使用ObjectList.Contains(foo)时对象列表无法正确比较的技术问题
解决C# List.Contains()无法正确判断对象存在的问题
我来帮你搞定这个问题!你遇到的核心原因是:默认情况下,List<T>.Contains()使用的是引用相等性判断——它只会检查两个对象是不是内存中的同一个实例,而不会去比较对象的内容。你的Book类没有重写Equals()和GetHashCode()方法,所以哪怕两本书的ISBN、标题完全一致,只要是不同的实例,Contains()就会返回false。
下面给你两种常用的解决方案,你可以根据场景选择:
方案一:重写Book类的Equals()和GetHashCode()方法
如果Book类是你自己维护的,优先用这个方法——这样所有涉及对象相等性的操作(比如Contains、IndexOf、Distinct等)都会自动按内容判断,一劳永逸。
因为ISBN是书籍的唯一标识,我们可以基于它来实现相等性逻辑:
public class Book { public string Title; public string Author; public string ISBN; public Book(string title, string author, string iSBN) { Title = title; Author = author; ISBN = iSBN; } // 重写Equals方法,基于ISBN判断对象内容是否相等 public override bool Equals(object obj) { // 如果是同一个实例,直接返回true if (ReferenceEquals(this, obj)) return true; // 检查obj是否是Book类型 if (obj is not Book otherBook) return false; // 忽略大小写比较ISBN(可根据业务需求调整为严格区分) return string.Equals(ISBN, otherBook.ISBN, StringComparison.OrdinalIgnoreCase); } // 必须同时重写GetHashCode,和Equals的逻辑保持一致(.NET的约定) public override int GetHashCode() { // 用ISBN的哈希值,确保Equals为true的对象哈希值相同 return ISBN?.ToLowerInvariant().GetHashCode() ?? 0; } }
关键说明:
- 为什么要同时重写
GetHashCode()?因为.NET的规则是:如果两个对象通过Equals()返回true,它们的GetHashCode()必须返回相同的值,否则会导致哈希集合(比如HashSet<T>)出现异常行为,虽然List<T>.Contains()不依赖哈希,但遵循约定是良好的编程习惯。 - 你可以根据业务需求调整相等性逻辑:比如如果需要同时比较
Title和Author,只需要在Equals()里添加对应的判断即可,但ISBN通常足够作为唯一标识。
方案二:使用自定义IEqualityComparer(无需修改Book类)
如果你无法修改Book类(比如它是第三方库中的类),可以用这个方法——通过传入自定义的相等性比较器,让Contains()按你的规则判断。
首先定义一个比较器类:
public class BookEqualityComparer : IEqualityComparer<Book> { public bool Equals(Book x, Book y) { if (x == null && y == null) return true; if (x == null || y == null) return false; // 同样基于ISBN判断,逻辑和方案一一致 return string.Equals(x.ISBN, y.ISBN, StringComparison.OrdinalIgnoreCase); } public int GetHashCode(Book obj) { return obj?.ISBN?.ToLowerInvariant().GetHashCode() ?? 0; } }
然后在调用Contains()时传入这个比较器:
// 假设你要检查的目标书籍对象是targetBook Book targetBook = ...; // 使用自定义比较器判断是否存在 bool exists = NewUsersBooks.Contains(targetBook, new BookEqualityComparer());
额外注意事项
- 字符串比较规则:示例中用了
StringComparison.OrdinalIgnoreCase忽略大小写,如果你的业务场景中ISBN是严格区分大小写的,改成StringComparison.Ordinal即可。 - null值处理:两个方案都考虑了null的情况,避免出现空引用异常。
内容的提问来源于stack exchange,提问作者Melvin Langvik
相关产品推荐
相关产品推荐

