C# 相等性检查

发布于 2024-10-07 01:56:50 字数 480 浏览 6 评论 0原文

您为您创建的结构编写相等性检查的方法是什么?

1)“完整”是否相等性检查需要大量样板代码(例如覆盖Equals覆盖GetHashCode、通用Equalsoperator==运算符!=)?

2) 您是否明确指定您的类对 IEquatable 接口进行建模?

3) 我是否理解正确,当我调用诸如 a == b 之类的内容时,没有实际的方法可以自动应用 Equals 覆盖我总是必须同时实现 Equalsoperator== 成员?

What's your approach on writing equality checks for the structs and classes you create?

1) Does the "full" equality checking require that much of boilerplate code (like override Equals, override GetHashCode, generic Equals, operator==, operator!=)?

2) Do you specify explicitly that your classes model the IEquatable<T> interface?

3) Do I understand correctly, that there is no actual way to automatically apply Equals overrides, when I invoke something like a == b and I always have to implement both the Equals and operator== members?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

带刺的爱情 2024-10-14 01:56:50

你是对的,这是很多样板代码,你需要单独实现所有内容。

我建议:

  • 如果您要实现值相等,请覆盖 GetHashCodeEquals(object) - 创建 == 的重载并实现 IEquatableT> 如果不这样做可能会导致非常意外的行为,
  • 如果您要覆盖 Equals(object),我总是会实现 IEquatable GetHashCode
  • 我很少重载 == 运算符,
  • 为未密封的类正确实现相等性很棘手,并且仍然会产生令人惊讶/不期望的结果。如果您需要层次结构中的类型相等,请实现 IEqualityComparer 来表达您感兴趣的比较。
  • 可变类型的相等通常是一个坏主意,因为两个对象可以相等然后不相等后来......如果一个对象在用作哈希表中的键后发生变异(以影响相等的方式),您将无法再次找到它。
  • 有些样板与结构略有不同......但像 Marc 一样,我很少编写自己的结构。

这是一个示例实现:

using System;

public sealed class Foo : IEquatable<Foo>
{
    private readonly string name;
    public string Name { get { return name; } }

    private readonly int value;
    public int Value { get { return value; } }

    public Foo(string name, int value)
    {
        this.name = name;
        this.value = value;
    }

    public override bool Equals(object other)
    {
        return Equals(other as Foo);
    }

    public override int GetHashCode()
    {
        int hash = 17;
        hash = hash * 31 + (name == null ? 0 : name.GetHashCode());
        hash = hash * 31 + value;
        return hash;
    }

    public bool Equals(Foo other)
    {
        if ((object) other == null)
        {
            return false;
        }
        return name == other.name && value == other.value;
    }

    public static bool operator ==(Foo left, Foo right)
    {
        return object.Equals(left, right);
    }

    public static bool operator !=(Foo left, Foo right)
    {
        return !(left == right);
    }
}

是的,这是大量的样板文件,实现之间的变化很少:(

== 的实现稍微比可能是这样,因为它会调用 Equals(object) ,它需要进行动态类型检查......但替代方案甚至更加样板,如下所示:

public static bool operator ==(Foo left, Foo right)
{
    if ((object) left == (object) right)
    {
        return true;
    }

    // "right" being null is covered in left.Equals(right)
    if ((object) left == null)
    {
        return false;
    }
    return left.Equals(right);
}

You're right, this is a lot of boiler-plate code, and you need to implement everything separately.

I would recommend:

  • If you're going to implement value equality at all, override GetHashCode and Equals(object) - creating overloads for == and implementing IEquatable<T> without doing that could result in very unexpected behaviour
  • I would always implement IEquatable<T> if you're overriding Equals(object) and GetHashCode
  • I only overload the == operator more rarely
  • Implementing equality correctly for unsealed classes is tricky, and can still produce surprising/undesirable results. If you need equality for types in a hierarchy, implement IEqualityComparer<T> expressing the comparison you're interested in.
  • Equality for mutable types is usually a bad idea, as two objects can be equal and then unequal later... if an object is mutated (in an equality-affecting way) after it's been used as a key in a hash table, you won't be able to find it again.
  • Some of the boiler-plate is slightly different for structs... but like Marc, I very rarely write my own structs.

Here's a sample implementation:

using System;

public sealed class Foo : IEquatable<Foo>
{
    private readonly string name;
    public string Name { get { return name; } }

    private readonly int value;
    public int Value { get { return value; } }

    public Foo(string name, int value)
    {
        this.name = name;
        this.value = value;
    }

    public override bool Equals(object other)
    {
        return Equals(other as Foo);
    }

    public override int GetHashCode()
    {
        int hash = 17;
        hash = hash * 31 + (name == null ? 0 : name.GetHashCode());
        hash = hash * 31 + value;
        return hash;
    }

    public bool Equals(Foo other)
    {
        if ((object) other == null)
        {
            return false;
        }
        return name == other.name && value == other.value;
    }

    public static bool operator ==(Foo left, Foo right)
    {
        return object.Equals(left, right);
    }

    public static bool operator !=(Foo left, Foo right)
    {
        return !(left == right);
    }
}

And yes, that's a heck of a lot of boilerplate, very little of which changes between implementations :(

The implementation of == is slightly less efficient than it might be, as it will call through to Equals(object) which needs to do the dynamic type check... but the alternative is even more boiler-plate, like this:

public static bool operator ==(Foo left, Foo right)
{
    if ((object) left == (object) right)
    {
        return true;
    }

    // "right" being null is covered in left.Equals(right)
    if ((object) left == null)
    {
        return false;
    }
    return left.Equals(right);
}
你是我的挚爱i 2024-10-14 01:56:50

我很少在课堂上做任何特别的事情;对于大多数常规对象,引用相等效果很好。

我什至很少写struct;但由于结构代表,因此通常适合提供相等性等。这通常会涉及所有内容; Equals、==、!= 和 IEquatable(因为这可以避免在使用 EqualityComparer.Default 的场景中进行装箱。

样板文件通常不会有太大问题,但是像 resharper 这样的 IIRC 工具可以在这里提供帮助。

是的,建议保持 Equals 和 == 同步,并且需要明确地完成此操作。

I rarely do anything special for classes; for most regular objects referential equality works fine.

I even more rarely write a struct; but since structs represent values it is usually appropriate to provide equality etc. This would usually involve everything; Equals, ==, != and IEquatable<T> (since this avoids boxing in scenarios using EqualityComparer<T>.Default.

The boilerplate isn't usually too problematic, but IIRC tools like resharper can help here.

Yes, it is advisable to keep Equals and == in sync, and this needs to be done explicitely.

喜爱皱眉﹌ 2024-10-14 01:56:50

您只需要为 a==b 实现operator==。
由于我喜欢字典中的数据,有时我会重写 GetHashCode。

接下来,我实现 Equals(作为未提及的标准...这是因为使用泛型时没有相等性约束)并指定实现 IEquatable。既然我要这样做,我不妨将我的 == 和 != 实现指向 Equals。 :)

You just need to implement operator== for a==b .
As I like my data in dictionaries sometimes I override GetHashCode.

Next I implement Equals (as an unmentioned standard ... this is because there is no constraint for equality when using generics) and specify implementing IEquatable. Since I am going to do this I might as well point my == and != implementations to Equals. :)

捂风挽笑 2024-10-14 01:56:50

See What is "Best Practice" For Comparing Two Instances of a Reference Type?

You can avoid boiler plate code (hope C#/VS team brings something easy for developers in their next iteration) with the help of a snippet, here is one such..

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文