Distinct 不适用于 IEquatable<T>

Distinct is not working with IEquatable<T>

提问人:Mohamad Hammash 提问时间:1/31/2023 更新时间:2/1/2023 访问量:81

问:

我有一个类 Bar,如下所示:

public class Bar : IEquatable<Bar>
{
    public string Stringbar1{ get; set; }
    public string Stringbar2{ get; set; }
    public string Stringbar3{ get; set; }
    public string Stringbar4{ get; set; }

    [JsonConverter(typeof(StringEnumConverter))]
    public EnumFoo1 Enumfoo1{ get; set; }

    public bool IsBar{ get; set; }
    public List<string> StringBars{ get; set; }
    [BsonSerializer(SerializerType = typeof(NullableDateTimeOffsetToUtcSerializer))]
    public DateTimeOffset? FooDate{ get; set; }
    public string Stringbar5{ get; set; }
    [JsonConverter(typeof(StringEnumConverter))]
    public EnumFoo2 EnumFoo2 { get; set; }
    public string StringBar6{ get; set; }
    public int Foo{ get; set; }
 
    public Bar()
    {
        EnumFoo1= EnumFoo1.Unknown;
        EnumFoo2= EnumFoo2.Other;
        StringBars= new List<string>();
    }

 public bool Equals(Bar other)
    {
        if (other == null)
        {
            return false;
        }

        return Stringbar1 == other.Stringbar1&& Stringbar2== other.Stringbar2 && Stringbar3== other.Stringbar3 && Stringbar4== other.Stringbar4 && EnumFoo1== other.EnumFoo1 && IsBar== other.IsBar&&  BothNullOrEquals(StringBars,other.StringBars) && StringBar5==other.StringBar5&& FooDate== other.FooDate && ContractType == other.ContractType && LotNumber == other.LotNumber && Rank == other.Rank;
    }


    public override int GetHashCode()
    {
        var stringbar1Hashcode = Stringbar1== null ? 0 : Stringbar1.GetHashCode();
        var stringbar2HashCode = Stringbar2== null ? 0 : Stringbar2.GetHashCode();
        var stringbar3CodeHashCode = Stringbar3== null ? 0 : Stringbar3.GetHashCode();
        var EnumFoo1HashCode =  EnumFoo1.GetHashCode();
        var Stringbar4HashCode = Stringbar4== null ? 0 : Stringbar4.GetHashCode();
        var isBarHashCode =  IsBar.GetHashCode();
        var strtingBarsHashCode = StringBars== null ? 0 : StringBars.GetHashCode();
        var stringbar5HashCode = Stringbar5== null ? 0 : Stringbar5.GetHashCode();
        var fooDateHashCode = FooDate== null ? 0 : FooDate.GetHashCode();
        var enumFoo2HashCode= EnumFoo2.GetHashCode();
         var stringBar6HasCode = StringBar6== null ? 0 : StringBar6.GetHashCode();
        var fooHashCode= Foo.GetHashCode();
        return stringbar1Hashcode ^ stringbar2HashCode ^ stringbar3CodeHashCode ^ EnumFoo1HashCode ^ Stringbar4HashCode ^ isBarHashCode ^ strtingBarsHashCode ^ stringbar5HashCode ^ fooDateHashCode ^ enumFoo2HashCode ^ stringBar6HasCode  ^ fooHashCode ;
    }
    
    
    public static bool BothNullOrEquals<T>(IEnumerable<T> left, IEnumerable<T> right)
    {
        if (left == null && right == null)
        {
            return true;
        }
        if (left != null && right != null)
        {
            return left.SequenceEqual(right);
        }
        return false;
    }
}

Equals 按预期工作,但似乎在 GetHashCode 方面我遗漏了一些东西,因为像 LINQ Distinct 这样的扩展方法没有按预期工作。我知道 Distinct 使用 GetHashCode 方法来比较引用,所以知道我做错了什么吗?

C# LINQ 非重复 性 GetHashCode IEQUATABLE

评论

0赞 Damien_The_Unbeliever 1/31/2023
您已覆盖但尚未覆盖 。您需要覆盖这两者。我怀疑是导致问题的原因。GetHashCodeEquals(Object)
2赞 Sergey Kudriavtsev 1/31/2023
您认为 LINQ Distinct 存在哪些问题?你说他们没有按预期工作,那么你的期望是什么,你得到了什么?
0赞 madreflection 1/31/2023
实际上,您不需要覆盖这两者。如果覆盖,则需要覆盖,但反之则不然。GetHashCodeEquals(Object)
0赞 Damien_The_Unbeliever 1/31/2023
@madreflection - 你可以这么说,但文档却不是这样说:“重写 GetHashCode() 的派生类也必须重写 Equals(Object)”。那里给出的理由听起来确实有点乱码,但这就是它所说的。
1赞 madreflection 2/1/2023
@MohamadHammash:为您提供保障:Microsoft.Bcl.HashCode

答:

1赞 Dmitry Bychenko 1/31/2023 #1

我能看到的问题是

var strtingBarsHashCode = StringBars== null ? 0 : StringBars.GetHashCode();

请注意,对于 HashCode 是基于引用的: 如果仅共享相同的引用,则哈希码保证相等。但是,您可以通过更轻松的方案来比较这些集合:List<string> StringBarsStringBars

BothNullOrEquals(StringBars, other.StringBars)

因此,不必共享相同的引用即可相等,它们 应该只有相等的项目。请注意,不能为相等的实例返回不同的代码。StringBarsGetHashCode

不要说得太详细;请注意,这应该是对 ;如此有选择性地具有选择性就足够了:GetHashCodeGetHashCodeEquals2 .. 3

public override int GetHashCode() {
  // Let .Net check for null and combine hash codes for you
  return HashCode.Combine(Stringbar1, Stringbar2, Stringbar3);
}

编辑:如果你不能使用 ,那就顺其自然吧,但是,请不要把很多字段和属性放到几个最有选择性的字段和属性中:HashCode.Combine^GetHashCode

public override int GetHashCode() {
  // Let .Net check for null and combine hash codes for you
  return (Stringbar1?.GetHashCode() ?? 0) ^
         (Stringbar2?.GetHashCode() ?? 0) ^
         (Stringbar3?.GetHashCode() ?? 0);
}

评论

0赞 Mohamad Hammash 2/1/2023
不幸的是,该应用程序使用的是不支持此功能的 .netstandard2.0。