在集合之间查找唯一对象
Posted
技术标签:
【中文标题】在集合之间查找唯一对象【英文标题】:Finding Unique Objects between collections 【发布时间】:2021-09-24 09:14:54 【问题描述】:我有一个测试,我创建了两个列表。一个代表我已经从某个来源(我无法控制)收集并收集的数据,另一个代表我的存储库中已经存在的已知数据。
它们看起来像这样:
var newAccounts = new[]
new Account
Id = 1,
SA_ID = 1,
SA_Name = "Sa_Name1",
RelationshipType = "new",
LE_ID = 1,
LE_GroupID = 1,
SiteID = 1,
MinDate = DateTime.Now.AddDays(-1),
MaxDate = DateTime.Now.AddDays(1),
DaysOn = 1,
Child1 = new List<Child1> new Child1
SiteID = 1,
MaxDate = DateTime.Today.AddDays(7),
MinDate = DateTime.Today.AddDays(1),
,
Child2 = new List<Child2>
new Child2
SA_ID = 1,
LastUpdate = DateTime.Now.AddDays(-1),
CommentText = "Account added",
Status = AccountStatus.AccountAdded.ToString(),
,
new Account
Id = 2,
SA_ID = 2,
SA_Name = "Sa_Name2",
RelationshipType = "new",
LE_ID = 2,
LE_GroupID = 2,
SiteID = 2,
MinDate = DateTime.Now.AddDays(-2),
MaxDate = DateTime.Now.AddDays(2),
DaysOn = 2,
,
new Account
Id = 3,
SA_ID = 3,
SA_Name = "Sa_Name3",
RelationshipType = "new",
LE_ID = 3,
LE_GroupID = 3,
SiteID = 3,
MinDate = DateTime.Now.AddDays(-3),
MaxDate = DateTime.Now.AddDays(3),
DaysOn = 3,
;
var knownAccounts = new[]
new Account
Id = 1,
SA_ID = 1,
SA_Name = "Sa_Name1",
RelationshipType = "new",
LE_ID = 1,
LE_GroupID = 1,
SiteID = 1,
MinDate = DateTime.Now.AddDays(-1),
MaxDate = DateTime.Now.AddDays(1),
DaysOn = 1,
Child1 = new List<Child1> new Child1
SiteID = 1,
MaxDate = DateTime.Today.AddDays(7),
MinDate = DateTime.Today.AddDays(1),
,
Child2 = new List<Child2>
new Child2
SA_ID = 1,
LastUpdate = DateTime.Now.AddDays(-1),
CommentText = "Account added",
Status = AccountStatus.AccountAdded.ToString(),
;
在我的单元测试中,我想从newAccounts
中删除Account ID 1
,所以我的收藏中只剩下2 个条目。这些是我迄今为止的尝试:
public List<T> ReturnUniqueEntriesList<T>(List<T> newAccounts, List<T> knownAccounts)
var a = knownAccounts.Intersect(newAccounts).ToList();
var listA = newAccounts.Except(knownAccounts).ToList();
var listB = knownAccounts.Except(newAccounts).ToList();
var result = listB.Intersect(listA).ToList();
return result;
当我运行它时,最终结果为 0。a
也返回 0,listA
和 listB
只需返回它们各自的对象。
我在这里做错了什么/错过了什么?任何帮助将不胜感激
【问题讨论】:
对于相交/排除等操作,列表中的对象需要具有可比性。如果不实现(IComparable、IEquatable 等)任何内容,将使用内存地址。所以 knownAccount 和 newAccount 中的实例总是不同的,因为它们是不同的实例,即使它们包含相同的数据。 【参考方案1】:为 Account 覆盖 Equals 和 GetHashcode,使它们不依赖于默认实现(对象的内存地址)。这意味着 C# 将能够在执行例外时正确地将它们等同起来。
例如:
public class Account
public override bool Equals(object other)
return other is Account a && a.Id == this.Id; //nb; is returns false if other is a null, even if it is an Account
public override int GetHashCode()
return Id.GetHashCode();
事实上,以下两个帐户非常不同:
var a = new Account Id = 1 ;
var b = new Account Id = 1 ;
..因为它们位于不同的内存地址。
通过重写 Equals 使其比较另一个的 Id,而不管其他属性,然后您基本上可以实现您似乎描述的“具有相同 ID 的两个帐户对象是等效的”的情况
如果其他属性因素影响决策,也添加这些属性。 Hashcode.Combine 是一种有用的方法,可以将多个哈希码组合起来,以解决获取多个属性的哈希码以产生合适的新信号哈希码的难题 - https://docs.microsoft.com/en-us/dotnet/api/system.hashcode.combine?view=net-5.0
【讨论】:
是的,Enumerable.Except Method 的备注部分说 “默认相等比较器 Default,用于比较类型的值。要比较自定义数据类型,您需要覆盖Equals 和 GetHashCode 方法,并可选择在自定义类型中实现 IEquatable以上是关于在集合之间查找唯一对象的主要内容,如果未能解决你的问题,请参考以下文章