web-dev-qa-db-ja.com

operator ==とEquals()のオーバーロード

私はC#プロジェクトに取り組んでいます。そのプロジェクトでは、これまで、不変オブジェクトとファクトリを使用して、タイプFooのオブジェクトが常に==と等しいかどうかを比較できるようにしています。

Fooオブジェクトは一度作成すると変更できません。ファクトリは常に、指定された引数のセットに対して同じオブジェクトを返します。これはうまく機能し、コードベース全体で、==が常に等しいかどうかのチェックに機能すると想定しています。

次に、これが常に機能するとは限らないEdgeケースを導入するいくつかの機能を追加する必要があります。最も簡単なことは、そのタイプのoperator ==をオーバーロードして、プロジェクト内の他のコードを変更する必要がないようにすることです。しかし、これはコードのにおいとして私を襲います:Equalsではなくoperator ==をオーバーロードすることは奇妙に思えるだけで、==が参照の等価性をチェックするという慣例に慣れており、Equalsはオブジェクトの等価性(または用語が何であれ)をチェックします。

これは正当な懸念事項ですか、それともoperator ==に過負荷をかけるべきですか?

44
JSBձոգչ

標準では、ほとんどのタイプで、.Equalsがオブジェクトの類似性をチェックし、演算子==が参照の等価性をチェックするというのが標準だと思います。

ベストプラクティスは、不変型の場合、演算子==.Equalsと同様に類似性をチェックする必要があるということです。そして、それらが本当に同じオブジェクトであるかどうかを知りたい場合は、.ReferenceEqualsを使用します。この例については、C#Stringクラスを参照してください。

28
McKay

overloading==overriding Equalsの間には大きな違いがあります。

あなたが表現を持っているとき

if (x == y) {

変数xとyを比較するために使用される方法は、コンパイル時に決定されます。これは演算子のオーバーロードです。 xとyを宣言するときに使用されるタイプは、それらを比較するために使用されるメソッドを定義するために使用されます。 xおよびy内の実際のタイプ(つまり、サブクラスまたはインターフェースの実装)は関係ありません。以下を検討してください。

object x = "hello";
object y = 'h' + "Ello"; // ensure it's a different reference

if (x == y) { // evaluates to FALSE

そして以下

string x = "hello";
string y = 'h' + "Ello"; // ensure it's a different reference

if (x == y) { // evaluates to TRUE

これは、変数xとyの宣言に使用される型が、==の評価に使用されるメソッドを決定するために使用されることを示しています。

比較すると、Equalsは、変数x内の実際の型に基づいてruntimeで決定されます。 Equalsは、他の型がオーバーライドできる、または実行できるObjectの仮想メソッドです。したがって、次の2つの例はどちらもtrueと評価されます。

object x = "hello";
object y = 'h' + "Ello"; // ensure it's a different reference

if (x.Equals(y)) { // evaluates to TRUE

そして以下

string x = "hello";
string y = 'h' + "Ello"; // ensure it's a different reference

if (x.Equals(y)) { // also evaluates to TRUE
89
Samuel Neff

それは間違いなくにおいがします。 _==_をオーバーロードするときは、Equals()GetHashCode()の両方にも一貫性があることを確認する必要があります。 MSDNガイドライン を参照してください。

そして、これがまったく問題ないと思われる唯一の理由は、型を不変として記述することです。

7
Henk Holterman

不変の型の場合、値の等価性をサポートするために_==_をオーバーロードすることに問題はないと思います。同じセマンティクスを持つようにEqualsをオーバーライドせずに_==_をオーバーライドすることはないと思います。 _==_をオーバーライドし、何らかの理由で参照の等価性を確認する必要がある場合は、Object.ReferenceEquals(a,b)を使用できます。

これを参照してください いくつかの有用なガイドラインについてはMicrosoftの記事

7
Mike Sackton

MSFTガイドライン (下記)に従ってこれを実装する方法を示すサンプル。 Equalsをオーバーライドする場合は、GetHashCode()もオーバーライドする必要があることに注意してください。これが人々の役に立つことを願っています。

public class Person
{
    public Guid Id { get; private set; }

    public Person(Guid id)
    {
        Id = id;
    }

    public Person()
    {
        Id = System.Guid.NewGuid();
    }

    public static bool operator ==(Person p1, Person p2)
    {
        bool rc;

        if (System.Object.ReferenceEquals(p1, p2))
        {
            rc = true;
        }
        else if (((object)p1 == null) || ((object)p2 == null))
        {
            rc = false;
        }
        else
        {
            rc = (p1.Id.CompareTo(p2.Id) == 0);
        }

        return rc;
    }

    public static bool operator !=(Person p1, Person p2)
    {
        return !(p1 == p2);
    }

    public override bool Equals(object obj)
    {
        bool rc = false;
        if (obj is Person)
        {
            Person p2 = obj as Person;
            rc = (this == p2);
        }
        return rc;
    }

    public override int GetHashCode()
    {
        return Id.GetHashCode();
    }
}
7
John

マイクロソフト独自のベストプラクティスによると、Equalsメソッドの結果と等号(==)オーバーロードは同じになるはずです。

CA2224:オーバーロード演算子が等しいときにオーバーライドが等しい

5
Ykok