C# 使用Linq与自定义IEqualityComparer相交
长话短说:我有两个对象集合。一个包含好的值(我们称之为“好”),另一个包含默认值(Mr.“default”)。我想要好与坏,坏与坏之间的交集。换句话说:Intersect(并集(良好,默认),Default)。有人可能会认为它会解析为默认值,但这里是它变得棘手的地方:我使用一个定制的IEqualityComparer 我上了以下课程:C# 使用Linq与自定义IEqualityComparer相交,c#,linq,intersection,intersect,C#,Linq,Intersection,Intersect,长话短说:我有两个对象集合。一个包含好的值(我们称之为“好”),另一个包含默认值(Mr.“default”)。我想要好与坏,坏与坏之间的交集。换句话说:Intersect(并集(良好,默认),Default)。有人可能会认为它会解析为默认值,但这里是它变得棘手的地方:我使用一个定制的IEqualityComparer 我上了以下课程: class MyClass { public string MyString1; public string MyString2; pub
class MyClass
{
public string MyString1;
public string MyString2;
public string MyString3;
}
class MyEqualityComparer : IEqualityComparer<MyClass>
{
public bool Equals(MyClass item1, MyClass item2)
{
if(item1 == null && item2 == null)
return true;
else if((item1 != null && item2 == null) ||
(item1 == null && item2 != null))
return false;
return item1.MyString1.Equals(item2.MyString1) &&
item1.MyString2.Equals(item2.MyString2);
}
public int GetHashCode(MyClass item)
{
return new { item.MyString1, item.MyString2 }.GetHashCode();
}
}
我教过它应该是有效的,但我得到的结果基本上只是好的{MyString1,MyString2}对集,但都来自默认集,所以我有所有的默认值。我还尝试切换上一次相交的默认值和良好值,但得到了相同的结果。首先,这是错误的:
public bool Equals(MyClass item1, MyClass item2)
{
return GetHashCode(item1) == GetHashCode(item2);
}
如果hashcode不同,则确保相应的2项不同,但如果它们相等,则不能保证相应的2项相等
因此,这是正确的等于实现:
public bool Equals(MyClass item1, MyClass item2)
{
if(object.ReferenceEquals(item1, item2))
return true;
if(item1 == null || item2 == null)
return false;
return item1.MyString1.Equals(item2.MyString1) &&
item1.MyString2.Equals(item2.MyString2);
}
作为(预期我),代码如下:
var Default = new List<MyClass>
{
new MyClass{MyString1="A",MyString2="A",MyString3="-"},
new MyClass{MyString1="B",MyString2="B",MyString3="-"},
new MyClass{MyString1="X",MyString2="X",MyString3="-"},
new MyClass{MyString1="Y",MyString2="Y",MyString3="-"},
new MyClass{MyString1="Z",MyString2="Z",MyString3="-"},
};
var Good = new List<MyClass>
{
new MyClass{MyString1="A",MyString2="A",MyString3="+"},
new MyClass{MyString1="B",MyString2="B",MyString3="+"},
new MyClass{MyString1="C",MyString2="C",MyString3="+"},
new MyClass{MyString1="D",MyString2="D",MyString3="+"},
new MyClass{MyString1="E",MyString2="E",MyString3="+"},
};
var wantedResult = Good.Intersect(Default, new MyEqualityComparer())
.Union(Default, new MyEqualityComparer());
// wantedResult:
// A A +
// B B +
// X X -
// Y Y -
// Z Z -
var Default=新列表
{
新MyClass{MyString1=“A”,MyString2=“A”,MyString3=“-”},
新MyClass{MyString1=“B”,MyString2=“B”,MyString3=“-”},
新MyClass{MyString1=“X”,MyString2=“X”,MyString3=“-”},
新MyClass{MyString1=“Y”,MyString2=“Y”,MyString3=“-”},
新MyClass{MyString1=“Z”,MyString2=“Z”,MyString3=“-”},
};
var Good=新列表
{
新MyClass{MyString1=“A”,MyString2=“A”,MyString3=“+”},
新MyClass{MyString1=“B”,MyString2=“B”,MyString3=“+”},
新MyClass{MyString1=“C”,MyString2=“C”,MyString3=“+”},
新MyClass{MyString1=“D”,MyString2=“D”,MyString3=“+”},
新MyClass{MyString1=“E”,MyString2=“E”,MyString3=“+”},
};
var wantedResult=Good.Intersect(默认值为new MyEqualityComparer())
.Union(默认值,新的MyEqualityComparer());
//想要的结果:
//A+
//B B+
//X X-
//Y Y-
//Z Z-
您需要检查实际的相等性,而不仅仅是hashcode相等性
GetHashCode()
不是(也不能)无冲突的,这就是为什么首先需要Equals
方法的原因
而且,你可以通过写作来更简单地做到这一点
WantedResult = Good.Concat(Default).Distinct();
Distinct
方法将返回每对副本的第一项,因此这将返回所需的结果
编辑:应该是
WantedResult = Good.Intersect(Default, new MyEqualityComparer())
.Union(Default, new MyEqualityComparer());
你的Equals实现非常糟糕。将出现不应该存在的哈希冲突。为什么不使用相同的投影(new{item.MyString1,item.MyString2}
)而调用Equals呢?我应该研究一下,这可能是问题的一部分。并集使用GetHashCode,相交使用Equals。我真的没有在那部分投入任何精力。ashamedi如果我使用Concat&Distinct,我会得到我想要的结果,但我也会得到一些好的集合中不在所需范围内的项(默认)。并集和交集操作是可交换的,因此它不会更改结果,但是是的,它避免使用中间人变量。哦,等等,并集和交集不是可交换的:(ABC u ABD)n AE=A
而(ABC n AE)u ABD=ABD
哦,对不起,我只是在考虑我的情况:(ABC u ABD)n ABC=ABC和ABC u(ABD n ABC)=ABC。但是,你是对的,它们不是。在我的情况下,(ABC u ABD)!=(ABD u ABC),因为它不是一个真正的联盟。+1感谢你在我的同龄人身上帮了我很多忙,但没有被接受,因为它不能解决问题。(我希望我能+2!:-P)没问题。我为你的交叉点问题写了正确的解决方案,但Slacks期待着我。他的答案;-)我觉得很愚蠢。。。我误解了有关intersect方法的工具提示,并且我的基础数据中存在错误,因此无法进行匹配。因为我认为这篇文章是最有帮助的,随着平等和例子的实施,我接受了它。再次感谢。不客气;-)。顺便说一下,如果比较器被调用了很多次,并且性能对您来说非常重要,那么您应该考虑重写GetHashCode()
,以避免为每次调用创建一个匿名类型的新实例。看看这个答案,可以得到一个快速有效的实现-->即Equals的实现是次优的。序言很难读。取而代之的是考虑<代码>(object .RealthyAs=(ITEM1,ITEM2))返回true;<代码>用于第一行。它处理两个都为null的情况,加快了两个都是相同引用的情况。如果(item1==null | | item2==null)返回false,则null检查的其余部分就是
更简短、更易于阅读,并且是正确的,因为如果它们都为null,则您已经返回true。
WantedResult = Good.Intersect(Default, new MyEqualityComparer())
.Union(Default, new MyEqualityComparer());