GroupBy HashSet 不分组,而 SetEquals 为真
GroupBy HashSet not grouping, whilst SetEquals is true
我有这样一种情况,我需要一个集合在 HashSet<myClass>
上 GroupBy
,其中 myClass
覆盖 Equals(myClass)
、Equals(object)
、GetHashCode()
、==
和 !=
.
当我执行 GroupBy()
时,结果没有分组。 Distinct()
也是如此。它是在对 myClass 的值调用 ToHashSet()
的大型 LINQ 查询中创建的。然后使用结果,其中生成的 HashSet 本身是 Dictionary<HashSet<myClass>, someOtherCollection>
.
的键
我已将问题提炼为最简单的情况,其中两个 HashSet<myClass>
、myHashSet1
和 myHashSet2
都只包含相同的单个元素。如果我调用 myHashSet1.Equals(myHashSet2)
它 returns false
,而 myHashSet1.SetEquals(myHashSet2)
returns true
.
这里做错了什么?当所有元素都匹配时,如何创建 GroupBy 组 HashSet?
可能的一步是
其中解释了如何覆盖 HashSet 的默认 IEqualityComparer。但如果这是答案的一部分,剩下的关键问题就是如何让 GroupBy 知道使用这个相等比较器?
我假设我应该在调用 ToHashSet()
时喂它,也许 ToHashSet(myHashSetEqualityComparer<myClass>)
,但它只需要 ToHashSet(IEqualityComparer<myClass>)
,而不是 ToHashSet(IEqualityComparer<HashSet<myClass>>)
这里是 myClass 的代码,提炼为要点:
public class myClass : myBaseClass, IEquatable<myClass>
{
public string Prop1 { get; set; }
public string Prop2 { get; set; }
public Guid Prop3 { get; set; }
public override bool Equals(myClass other)
{
if (Equals(other, null)) return false;
return (Prop1 == other.Prop1 && Prop2 == other.Prop2 && Prop3 == other.Prop3);
}
public override bool Equals(object obj)
{
if (Equals(obj, null) || !(obj is myClass))
return false;
return Equals((myClass)obj);
}
public static bool operator ==(myClass left, myClass right)
{
if (Object.Equals(left, null))
return (Object.Equals(right, null)) ? true : false;
else
return left.Equals(right);
}
public static bool operator !=(myClass left, myClass right)
{
return !(left == right);
}
public override int GetHashCode()
{
return Prop3.GetHashCode() + 31 * (Prop2.GetHashCode() + 31 * Prop1.GetHashCode());
}
}
注意:我之前问过这个问题,但是参考上面的链接问题关闭了,没有回答原始问题,明确指出这是关于 GroupBy 问题。我在此处添加了有关在 LINQ 中使用的更多详细信息以进行说明。
编辑 根据评论中的请求这就是我正在做的事情:
var myGroupedResult = myUngroupedCollection.
GroupBy(x => x.Value).
ToDictionary(x => x.Key, x => x.ToList());
// myUngroupedCollection is an IEnumerable<KeyValuePair<someClass, HashSet<myClass>>> produced by LINQ
// myGroupedResult is a Dictionary<HashSet<myClass>, List<someClass>>
我希望结果生成一个字典,其中键为 HashSet<myClass>
,值为 List<someClass>
。如果我有 5 个不同的哈希集,每个哈希集都有 10 次出现 someClass,我希望字典有 5 个键,每个键的值是一个包含 10 个元素的列表。相反,我得到一个包含 50 个键的字典,每个键的值是一个包含 1 个元素的列表。
我能够解决我的问题。在这里发布答案以防其他人遇到同样的问题。
解决方案有两个步骤。首先创建一个通用的IEqualityComparer<HashSet<T>>
(来自):
public class HashSetEqualityComparerBySetEquals<T> : IEqualityComparer<HashSet<T>>
{
public bool Equals(HashSet<T> x, HashSet<T> y)
{
if (ReferenceEquals(x, null))
return false;
return x.SetEquals(y);
}
public int GetHashCode(HashSet<T> set)
{
int hashCode = 0;
if (set != null)
{
foreach (T t in set)
{
hashCode = hashCode ^
(set.Comparer.GetHashCode(t) & 0x7FFFFFFF);
}
}
return hashCode;
}
}
然后在 GroupBy()
中提供它(提示来自这里:,它适用于 List,但不适用于 HashSet,它需要额外的 elementSelector
作为第二个参数):
HashSetEqualityComparerBySetEquals<myClass> comparer = new HashSetEqualityComparerBySetEquals<myClass>();
var myGroupedResult = myUngroupedCollection.
GroupBy(x => x.Value, x => x.Key, comparer).
ToDictionary(x => x.Key, x => x.ToList());
// myUngroupedCollection is an IEnumerable<KeyValuePair<someClass, HashSet<myClass>>> produced by LINQ but could be a Dictionary or another collection.
// myGroupedResult is a Dictionary<HashSet<myClass>, List<someClass>>
在执行其他检查相等性的 LINQ 操作时,也可以使用相同的 IEqualityComparer,例如 Distinct()
和 FirstOrDefault()
:
var thisWorksAsExpected = myGroupedResult.FirstOrDefault(x => comparer.Equals(x.Key, aHashSetWithSameElements));
var thisAlsoWorks = myGroupedResult.FirstOrDefault(x => x.Key.SetEquals(aHashSetWithSameElements));
var thisDoesNotWork = myGroupedResult.FirstOrDefault(x => x.Key == aHashSetWithSameElements);
// thisDoesNotWork returns null sometimes even when all elements match
我有这样一种情况,我需要一个集合在 HashSet<myClass>
上 GroupBy
,其中 myClass
覆盖 Equals(myClass)
、Equals(object)
、GetHashCode()
、==
和 !=
.
当我执行 GroupBy()
时,结果没有分组。 Distinct()
也是如此。它是在对 myClass 的值调用 ToHashSet()
的大型 LINQ 查询中创建的。然后使用结果,其中生成的 HashSet 本身是 Dictionary<HashSet<myClass>, someOtherCollection>
.
我已将问题提炼为最简单的情况,其中两个 HashSet<myClass>
、myHashSet1
和 myHashSet2
都只包含相同的单个元素。如果我调用 myHashSet1.Equals(myHashSet2)
它 returns false
,而 myHashSet1.SetEquals(myHashSet2)
returns true
.
这里做错了什么?当所有元素都匹配时,如何创建 GroupBy 组 HashSet?
可能的一步是
我假设我应该在调用 ToHashSet()
时喂它,也许 ToHashSet(myHashSetEqualityComparer<myClass>)
,但它只需要 ToHashSet(IEqualityComparer<myClass>)
,而不是 ToHashSet(IEqualityComparer<HashSet<myClass>>)
这里是 myClass 的代码,提炼为要点:
public class myClass : myBaseClass, IEquatable<myClass>
{
public string Prop1 { get; set; }
public string Prop2 { get; set; }
public Guid Prop3 { get; set; }
public override bool Equals(myClass other)
{
if (Equals(other, null)) return false;
return (Prop1 == other.Prop1 && Prop2 == other.Prop2 && Prop3 == other.Prop3);
}
public override bool Equals(object obj)
{
if (Equals(obj, null) || !(obj is myClass))
return false;
return Equals((myClass)obj);
}
public static bool operator ==(myClass left, myClass right)
{
if (Object.Equals(left, null))
return (Object.Equals(right, null)) ? true : false;
else
return left.Equals(right);
}
public static bool operator !=(myClass left, myClass right)
{
return !(left == right);
}
public override int GetHashCode()
{
return Prop3.GetHashCode() + 31 * (Prop2.GetHashCode() + 31 * Prop1.GetHashCode());
}
}
注意:我之前问过这个问题,但是参考上面的链接问题关闭了,没有回答原始问题,明确指出这是关于 GroupBy 问题。我在此处添加了有关在 LINQ 中使用的更多详细信息以进行说明。
编辑 根据评论中的请求这就是我正在做的事情:
var myGroupedResult = myUngroupedCollection.
GroupBy(x => x.Value).
ToDictionary(x => x.Key, x => x.ToList());
// myUngroupedCollection is an IEnumerable<KeyValuePair<someClass, HashSet<myClass>>> produced by LINQ
// myGroupedResult is a Dictionary<HashSet<myClass>, List<someClass>>
我希望结果生成一个字典,其中键为 HashSet<myClass>
,值为 List<someClass>
。如果我有 5 个不同的哈希集,每个哈希集都有 10 次出现 someClass,我希望字典有 5 个键,每个键的值是一个包含 10 个元素的列表。相反,我得到一个包含 50 个键的字典,每个键的值是一个包含 1 个元素的列表。
我能够解决我的问题。在这里发布答案以防其他人遇到同样的问题。
解决方案有两个步骤。首先创建一个通用的IEqualityComparer<HashSet<T>>
(来自
public class HashSetEqualityComparerBySetEquals<T> : IEqualityComparer<HashSet<T>> { public bool Equals(HashSet<T> x, HashSet<T> y) { if (ReferenceEquals(x, null)) return false; return x.SetEquals(y); } public int GetHashCode(HashSet<T> set) { int hashCode = 0; if (set != null) { foreach (T t in set) { hashCode = hashCode ^ (set.Comparer.GetHashCode(t) & 0x7FFFFFFF); } } return hashCode; } }
然后在 GroupBy()
中提供它(提示来自这里:elementSelector
作为第二个参数):
HashSetEqualityComparerBySetEquals<myClass> comparer = new HashSetEqualityComparerBySetEquals<myClass>();
var myGroupedResult = myUngroupedCollection.
GroupBy(x => x.Value, x => x.Key, comparer).
ToDictionary(x => x.Key, x => x.ToList());
// myUngroupedCollection is an IEnumerable<KeyValuePair<someClass, HashSet<myClass>>> produced by LINQ but could be a Dictionary or another collection.
// myGroupedResult is a Dictionary<HashSet<myClass>, List<someClass>>
在执行其他检查相等性的 LINQ 操作时,也可以使用相同的 IEqualityComparer,例如 Distinct()
和 FirstOrDefault()
:
var thisWorksAsExpected = myGroupedResult.FirstOrDefault(x => comparer.Equals(x.Key, aHashSetWithSameElements));
var thisAlsoWorks = myGroupedResult.FirstOrDefault(x => x.Key.SetEquals(aHashSetWithSameElements));
var thisDoesNotWork = myGroupedResult.FirstOrDefault(x => x.Key == aHashSetWithSameElements);
// thisDoesNotWork returns null sometimes even when all elements match