核心问题:List<T> 中的 T 为引用类型时,Contains() 默认比较的是引用地址而非对象内容,导致即使属性值完全相同的对象也会被判定为"不相等"。
🔍 问题背景
// 两个内容相同但引用不同的对象
var user1 = new UserInfo { UserName = "aaa", Age = 20 };
var user2 = new UserInfo { UserName = "aaa", Age = 20 };
Console.WriteLine(user1 == user2); // False(引用比较)
Console.WriteLine(list.Contains(user2)); // False(默认行为)根本原因:Contains() 方法内部调用的是 Equals() 方法,而引用类型默认实现的是引用相等性比较。
✅ 解决方案:重写 Equals 和 GetHashCode
让类实现 IEquatable<T> 接口,并重写相关方法,实现基于值的相等性判断:
public classUserInfo : IEquatable<UserInfo>
{
publicstring UserName { get; set; }
publicint Age { get; set; }
// 🔹 重写 Object.Equals(object)
public override bool Equals(object obj)
{
if (obj == null) returnfalse;
if (obj is UserInfo usr)
return Equals(usr);
returnfalse;
}
// 🔹 重写 GetHashCode(⚠️ 必须与 Equals 配套重写)
public override int GetHashCode()
{
// 推荐方式:使用 HashCode.Combine(.NET Core 2.1+)
// return HashCode.Combine(UserName, Age);
// 兼容方式:异或组合(注意空值处理)
return (UserName?.GetHashCode() ?? 0) ^ Age.GetHashCode();
}
// 🔹 实现 IEquatable<T>.Equals(T)
public bool Equals(UserInfo other)
{
if (other == null) returnfalse;
return UserName == other.UserName && Age == other.Age;
}
}
★⚠️ 重要提醒:
GetHashCode()必须与Equals()逻辑一致:相等的对象必须返回相同的哈希码注意 UserName可能为null,需做空值保护若用于 HashSet、Dictionary等哈希集合,GetHashCode的实现尤为关键
🧪 使用示例:对比两个 List
static void Main(string[] args)
{
var users1 = new List<UserInfo>
{
new UserInfo { UserName = "aaa", Age = 20 },
new UserInfo { UserName = "bbb", Age = 30 },
};
var users2 = new List<UserInfo>
{
new UserInfo { UserName = "aaa", Age = 20 },
new UserInfo { UserName = "bbb", Age = 40 }, // Age 不同
};
// 找出 users1 中有、users2 中没有的元素
users1.ForEach(m =>
{
if (!users2.Contains(m))
Console.WriteLine($"users1 有,users2 无: {m.UserName},{m.Age}");
});
// 输出: bbb,30
// 找出 users2 中有、users1 中没有的元素
users2.ForEach(m =>
{
if (!users1.Contains(m))
Console.WriteLine($"users2 有,users1 无: {m.UserName},{m.Age}");
});
// 输出: bbb,40
Console.ReadKey();
}
💡 进阶建议(可选方案)
重写 Equals/GetHashCode 实现 IEqualityComparer 使用 LINQ + Select + Join .NET 6+ record 类型
📌 示例:使用自定义比较器(不修改原类)
public classUserInfoComparer : IEqualityComparer<UserInfo>
{
public bool Equals(UserInfo x, UserInfo y)
{
return x?.UserName == y?.UserName && x?.Age == y?.Age;
}
public int GetHashCode(UserInfo obj)
{
return (obj.UserName?.GetHashCode() ?? 0) ^ obj.Age.GetHashCode();
}
}
// 使用方式:
if (!users2.Contains(m, new UserInfoComparer())) { ... }
总结:当 List<T> 的元素为自定义引用类型时,若需基于内容而非引用进行 Contains、Remove、Distinct 等操作,必须重写 Equals 和 GetHashCode,或提供自定义 IEqualityComparer<T>。这是 C# 集合操作中常见且关键的实践点。