C#.Equals()、. ReferenceEquals()および==演算子


84

これら3つについての私の理解は次のとおりです。

  • .Equals()データの同等性をテストします(より適切な説明がないため)。.Equals()同じオブジェクトの異なるインスタンスに対してTrueを返すことができます。これは、最も一般的にオーバーライドされるメソッドです。

  • .ReferenceEquals() 2つのオブジェクトが同じインスタンスであり、オーバーライドできないかどうかをテストします。

  • ==ReferenceEquals()デフォルトと同じですが、これはオーバーライドできます。

しかし、C#ステーションは次のように述べています。

オブジェクトクラスでは、EqualsReferenceEqualsメソッドは意味的に同等ですが、ReferenceEqualsオブジェクトインスタンスでのみ機能する点が異なります。 ReferenceEqualsこの方法は、静的です。

今はわかりません。誰かがこれに光を当てることができますか?



このトピックに関するstackoverflow.com/questions/814878/…および他の多くのStackOverflowの質問を参照してください。
イアンマーサー

@High私は持っています。私を混乱させているのは、C#ステーションから抽出した部分だけです。
999999 2010年

回答:


87

混乱の原因は、C#ステーションからの抽出にタイプミスがあることであるようです。「...ただし、Equalsはオブジェクトインスタンスでのみ機能します。ReferenceEqualsメソッドは静的です。」


それぞれのセマンティックな意味の違いについては大まかに正しいです(「同じオブジェクトの異なるインスタンス」は少し混乱しているように見えますが、おそらく「同じタイプの異なるインスタンス」と読む必要があります)。これについてはオーバーライドできます。

それを脇に置いておく場合は、質問の最後の部分、つまり、プレーンSystem.ObjectインスタンスとSystem.Object参照でどのように機能するかを扱いましょう(の非多形性をかわすために両方が必要です==)。ここでは、3つの操作すべてが同等に機能しますが、注意点Equalsがありますnull。で呼び出すことはできません。

Equals1つのパラメーター(可能性がありますnull)を受け取るインスタンスメソッドです。これはインスタンスメソッド(実際のオブジェクトで呼び出す必要があります)であるため、null-referenceで呼び出すことはできません。

ReferenceEqualsは、2つのパラメーターを受け取る静的メソッドですnull。静的(オブジェクトインスタンスに関連付けられていない)であるため、NullReferenceExceptionどのような状況でもをスローしません。

==は演算子であり、この場合(object)はReferenceEquals。と同じように動作します。NullReferenceExceptionどちらもスローしません。

説明する:

object o1 = null;
object o2 = new object();

//Technically, these should read object.ReferenceEquals for clarity, but this is redundant.
ReferenceEquals(o1, o1); //true
ReferenceEquals(o1, o2); //false
ReferenceEquals(o2, o1); //false
ReferenceEquals(o2, o2); //true

o1.Equals(o1); //NullReferenceException
o1.Equals(o2); //NullReferenceException
o2.Equals(o1); //false
o2.Equals(o2); //true

それで、上で引用したC#ステーションからの抜粋は間違っています.Equals()か(特にオーバーライドする場合)?
999999 2010年

1
抜粋はobjectクラス内」と述べています。その部分を飛ばしたと思いますか?そうでなければ、あなたはそれをオーバーライドすることについて話していません。
ドメニック2010年

1
私の答えはクラスについてだけobjectです。
ani

@Ani:以下の文が間違っていた静的メソッドはNullReferenceExceptionをスローする可能性があります:静的(オブジェクトインスタンスに関連付けられていない)であるため、どのような状況でもaNullReferenceExceptionをスローしません。
selvaraj 2010年

2
Equalsは、object2つのパラメーターを受け取る静的メソッドでもあります。その場合、一方または両方をnull
ウェストン2012年

20

見ていこのMSDNの記事主題にします。

関連するポイントは次のとおりです。

参照が等しいかどうかを確認するには、ReferenceEqualsを使用します。値が等しいかどうかを確認するには、EqualsまたはEqualsを使用します。

デフォルトでは、演算子==は、2つの参照が同じオブジェクトを示しているかどうかを判断することによって参照の同等性をテストするため、この機能を取得するために参照型で演算子==を実装する必要はありません。型が不変である場合、つまりインスタンスに含まれるデータを変更できない場合、演算子==をオーバーロードして、参照の同等性の代わりに値の同等性を比較すると便利です。不変のオブジェクトとして、それらが同じ値。

お役に立てれば!


6
残念ながら、リンクは切れています。関連情報をコピーするための+1。
pac0 2017

6

.ReferenceEqualsの理解は正しいです。

.Equalsは、値型のデータの同等性をチェックし、非値型(一般オブジェクト)の参照の同等性をチェックします。

.Equalsは、オブジェクトが何らかの形式のデータ等価性チェックを実行するためにオーバーライドできます

編集:また、.ReferenceEqualsは値型には使用できません(使用できますが、常にfalseになります)


3

「null」との比較について5セントを追加したいと思います。

  1. ReferenceEquals(object、object)は "(object)arg1 == arg2"と同じです(したがって、値型の場合、ボックス化され、時間がかかります)。しかし、この方法は、次のようないくつかの状況で引数のnullをチェックする唯一の100%安全な方法です。

    • a)を介してメンバーを呼び出す前。オペレーター
    • b)AS演算子の結果を確認します。
  2. ==およびEquals()。ReferenceEqualsはnullチェックで100%安全だと言っているのはなぜですか?コアクロスプロジェクトライブラリにジェネリック拡張機能を記述し、ジェネリックパラメータータイプを特定のドメインタイプに制限するとします。このタイプは、「==」演算子を導入する可能性があります-現在または後で(そして私を信じて、私は多くを見てきましたが、この演算子は、特にドメインまたは永続オブジェクトに関しては、非常に「奇妙な」ロジックを持つことができます)。引数のnullをチェックしてから、その引数に対してメンバー操作を呼び出そうとします。驚いたことに、ここでNullRefを使用できます。==演算子はEquals()とほぼ同じであるため、非常にカスタムで非常に予測不可能です。ただし、考慮すべき違いがあります。ジェネリックパラメーターを特定のカスタム型に制限しない場合(==は、型が「クラス」の場合にのみ使用できます)、==演算子はオブジェクトと同じです。 。ReferenceEquals(..)。等号の実装は、仮想であるため、常に最終タイプから使用されます。

したがって、独自の型を作成する場合、またはよく知られている型から派生する場合は、==を使用してnullをチェックすることをお勧めします。それ以外の場合は、object.ReferenceEquals(arg、null)を使用します。


1

Objectクラスでは、.Equalsは、平等ではなくIDを実装します。参照が等しいかどうかをチェックします。コードは次のようになります。

public virtual Boolean Equals(Object other) {
    if (this == other) return true;
    return false;
}

クラスに.Equalsを実装するときは、基本クラスを呼び出す必要があります。基本クラスがObjectでない場合にのみ.Equalsを呼び出します。ええ、それは複雑です。

さらに、派生クラスは.Equalsをオーバーライドできるため、それを呼び出してIDを確認することはできません。Microsoftが静的な.ReferenceEqualsメソッドを追加しました。

あるクラスを使用する場合、論理的に.Equalsは同等性をチェックし、.ReferenceEqualsはIDをチェックします。


1

Aniの優れた回答を拡張して、参照型とオーバーライドされた等価メソッドを処理する際の主な違いを示しました。

  • このコードの動作バージョンは、https//dotnetfiddle.net/dFKMhBで確認できます
  • または、このコードをLinqPadに貼り付けて、として実行しLanguage: C# Programます。

void Main()
{

    //odd os are null; evens are not null
    object o1 = null;
    object o2 = new object();
    object o3 = null;
    object o4 = new object();
    object o5 = o1;
    object o6 = o2;

    Demo d1 = new Demo(Guid.Empty);
    Demo d2 = new Demo(Guid.NewGuid());
    Demo d3 = new Demo(Guid.Empty);

    Debug.WriteLine("comparing null with null always yields true...");
    ShowResult("ReferenceEquals(o1, o1)", () => ReferenceEquals(o1, o1)); //true
    ShowResult("ReferenceEquals(o3, o1)", () => ReferenceEquals(o3, o1)); //true
    ShowResult("ReferenceEquals(o5, o1)", () => ReferenceEquals(o5, o1)); //true 
    ShowResult("o1 == o1", () => o1 == o1); //true
    ShowResult("o3 == o1", () => o3 == o1); //true
    ShowResult("o5 == o1", () => o5 == o1); //true 

    Debug.WriteLine("...though because the object's null, we can't call methods on the object (i.e. we'd get a null reference exception).");
    ShowResult("o1.Equals(o1)", () => o1.Equals(o1)); //NullReferenceException
    ShowResult("o1.Equals(o2)", () => o1.Equals(o2)); //NullReferenceException
    ShowResult("o3.Equals(o1)", () => o3.Equals(o1)); //NullReferenceException
    ShowResult("o3.Equals(o2)", () => o3.Equals(o2)); //NullReferenceException
    ShowResult("o5.Equals(o1)", () => o5.Equals(o1));  //NullReferenceException
    ShowResult("o5.Equals(o2)", () => o5.Equals(o1));  //NullReferenceException

    Debug.WriteLine("Comparing a null object with a non null object always yeilds false");
    ShowResult("ReferenceEquals(o1, o2)", () => ReferenceEquals(o1, o2)); //false
    ShowResult("ReferenceEquals(o2, o1)", () => ReferenceEquals(o2, o1)); //false
    ShowResult("ReferenceEquals(o3, o2)", () => ReferenceEquals(o3, o2)); //false
    ShowResult("ReferenceEquals(o4, o1)", () => ReferenceEquals(o4, o1)); //false
    ShowResult("ReferenceEquals(o5, o2)", () => ReferenceEquals(o3, o2)); //false
    ShowResult("ReferenceEquals(o6, o1)", () => ReferenceEquals(o4, o1)); //false
    ShowResult("o1 == o2)", () => o1 == o2); //false
    ShowResult("o2 == o1)", () => o2 == o1); //false
    ShowResult("o3 == o2)", () => o3 == o2); //false
    ShowResult("o4 == o1)", () => o4 == o1); //false
    ShowResult("o5 == o2)", () => o3 == o2); //false
    ShowResult("o6 == o1)", () => o4 == o1); //false
    ShowResult("o2.Equals(o1)", () => o2.Equals(o1)); //false
    ShowResult("o4.Equals(o1)", () => o4.Equals(o1)); //false
    ShowResult("o6.Equals(o1)", () => o4.Equals(o1)); //false

    Debug.WriteLine("(though again, we can't call methods on a null object:");
    ShowResult("o1.Equals(o2)", () => o1.Equals(o2)); //NullReferenceException
    ShowResult("o1.Equals(o4)", () => o1.Equals(o4)); //NullReferenceException
    ShowResult("o1.Equals(o6)", () => o1.Equals(o6)); //NullReferenceException

    Debug.WriteLine("Comparing 2 references to the same object always yields true");
    ShowResult("ReferenceEquals(o2, o2)", () => ReferenceEquals(o2, o2)); //true    
    ShowResult("ReferenceEquals(o6, o2)", () => ReferenceEquals(o6, o2)); //true <-- Interesting
    ShowResult("o2 == o2", () => o2 == o2); //true  
    ShowResult("o6 == o2", () => o6 == o2); //true <-- Interesting
    ShowResult("o2.Equals(o2)", () => o2.Equals(o2)); //true 
    ShowResult("o6.Equals(o2)", () => o6.Equals(o2)); //true <-- Interesting

    Debug.WriteLine("However, comparing 2 objects may yield false even if those objects have the same values, if those objects reside in different address spaces (i.e. they're references to different objects, even if the values are similar)");
    Debug.WriteLine("NB: This is an important difference between Reference Types and Value Types.");
    ShowResult("ReferenceEquals(o4, o2)", () => ReferenceEquals(o4, o2)); //false <-- Interesting
    ShowResult("o4 == o2", () => o4 == o2); //false <-- Interesting
    ShowResult("o4.Equals(o2)", () => o4.Equals(o2)); //false <-- Interesting

    Debug.WriteLine("We can override the object's equality operator though, in which case we define what's considered equal");
    Debug.WriteLine("e.g. these objects have different ids, so we treat as not equal");
    ShowResult("ReferenceEquals(d1,d2)",()=>ReferenceEquals(d1,d2)); //false
    ShowResult("ReferenceEquals(d2,d1)",()=>ReferenceEquals(d2,d1)); //false
    ShowResult("d1 == d2",()=>d1 == d2); //false
    ShowResult("d2 == d1",()=>d2 == d1); //false
    ShowResult("d1.Equals(d2)",()=>d1.Equals(d2)); //false
    ShowResult("d2.Equals(d1)",()=>d2.Equals(d1)); //false
    Debug.WriteLine("...whilst these are different objects with the same id; so we treat as equal when using the overridden Equals method...");
    ShowResult("d1.Equals(d3)",()=>d1.Equals(d3)); //true <-- Interesting (sort of; different to what we saw in comparing o2 with o6; but is just running the code we wrote as we'd expect)
    ShowResult("d3.Equals(d1)",()=>d3.Equals(d1)); //true <-- Interesting (sort of; different to what we saw in comparing o2 with o6; but is just running the code we wrote as we'd expect)
    Debug.WriteLine("...but as different when using the other equality tests.");
    ShowResult("ReferenceEquals(d1,d3)",()=>ReferenceEquals(d1,d3)); //false <-- Interesting (sort of; same result we had comparing o2 with o6; but shows that ReferenceEquals does not use the overridden Equals method)
    ShowResult("ReferenceEquals(d3,d1)",()=>ReferenceEquals(d3,d1)); //false <-- Interesting (sort of; same result we had comparing o2 with o6; but shows that ReferenceEquals does not use the overridden Equals method)
    ShowResult("d1 == d3",()=>d1 == d3); //false <-- Interesting (sort of; same result we had comparing o2 with o6; but shows that ReferenceEquals does not use the overridden Equals method)
    ShowResult("d3 == d1",()=>d3 == d1); //false <-- Interesting (sort of; same result we had comparing o2 with o6; but shows that ReferenceEquals does not use the overridden Equals method)


    Debug.WriteLine("For completeness, here's an example of overriding the == operator (wihtout overriding the Equals method; though in reality if overriding == you'd probably want to override Equals too).");
    Demo2 d2a = new Demo2(Guid.Empty);
    Demo2 d2b = new Demo2(Guid.NewGuid());
    Demo2 d2c = new Demo2(Guid.Empty);
    ShowResult("d2a == d2a", () => d2a == d2a); //true
    ShowResult("d2b == d2a", () => d2b == d2a); //false
    ShowResult("d2c == d2a", () => d2c == d2a); //true <-- interesting
    ShowResult("d2a != d2a", () => d2a != d2a); //false
    ShowResult("d2b != d2a", () => d2b != d2a); //true
    ShowResult("d2c != d2a", () => d2c != d2a); //false <-- interesting
    ShowResult("ReferenceEquals(d2a,d2a)", () => ReferenceEquals(d2a, d2a)); //true
    ShowResult("ReferenceEquals(d2b,d2a)", () => ReferenceEquals(d2b, d2a)); //false
    ShowResult("ReferenceEquals(d2c,d2a)", () => ReferenceEquals(d2c, d2a)); //false <-- interesting
    ShowResult("d2a.Equals(d2a)", () => d2a.Equals(d2a)); //true
    ShowResult("d2b.Equals(d2a)", () => d2b.Equals(d2a)); //false
    ShowResult("d2c.Equals(d2a)", () => d2c.Equals(d2a)); //false <-- interesting   

}



//this code's just used to help show the output in a friendly manner
public delegate bool Statement();
void ShowResult(string statementText, Statement statement)
{
    try 
    {
        Debug.WriteLine("\t{0} => {1}",statementText, statement());
    }
    catch(Exception e)
    {
        Debug.WriteLine("\t{0} => throws {1}",statementText, e.GetType());
    }
}

class Demo
{
    Guid id;
    public Demo(Guid id) { this.id = id; }
    public override bool Equals(object obj)
    {
        return Equals(obj as Demo); //if objects are of non-comparable types, obj will be converted to null
    }
    public bool Equals(Demo obj)
    {
        if (obj == null)
        {
            return false;
        }
        else
        {
            return id.Equals(obj.id);
        }
    }
    //if two objects are Equal their hashcodes must be equal
    //however, if two objects hash codes are equal it is not necessarily true that the objects are equal
    //i.e. equal objects are a subset of equal hashcodes
    //more info here: https://stackoverflow.com/a/371348/361842
    public override int GetHashCode()
    {
        return id.GetHashCode();
    }
}

class Demo2
{
    Guid id;
    public Demo2(Guid id)
    {
        this.id = id;
    }

    public static bool operator ==(Demo2 obj1, Demo2 obj2)
    {
        if (ReferenceEquals(null, obj1)) 
        {
            return ReferenceEquals(null, obj2); //true if both are null; false if only obj1 is null
        }
        else
        {
            if(ReferenceEquals(null, obj2)) 
            {
                return false; //obj1 is not null, obj2 is; therefore false
            }
            else
            {
                return obj1.id == obj2.id; //return true if IDs are the same; else return false
            }
        }
    }

    // NB: We also HAVE to override this as below if overriding the == operator; this is enforced by the compiler.  However, oddly we could choose to override it different to the below; but typically that would be a bad idea...
    public static bool operator !=(Demo2 obj1, Demo2 obj2)
    {
        return !(obj1 == obj2);
    }
}

-3

Equals()基になるタイプ(値/参照)に応じてハッシュコードまたは同等性ReferenceEquals()をチェックし、常にハッシュコードをチェックすることを目的としています。両方のオブジェクトが同じメモリ位置を指している場合にReferenceEquals戻りますtrue

double e = 1.5;
double d = e;
object o1 = d;
object o2 = d;

Console.WriteLine(o1.Equals(o2)); // True
Console.WriteLine(Object.Equals(o1, o2)); // True
Console.WriteLine(Object.ReferenceEquals(o1, o2)); // False

Console.WriteLine(e.Equals(d)); // True
Console.WriteLine(Object.Equals(e, d)); // True
Console.WriteLine(Object.ReferenceEquals(e, d)); // False

3
これはナンセンスです。EqualsもReferenceEqualsもHashCodeを調べません。HashCodes ofEqualsオブジェクトが等しくなければならないという要件があります。そして、オブジェクトはどこも指しません... ReferenceEqualsは、その引数の両方が同じ参照オブジェクトであるか、両方がnullである場合にのみtrueになります。
ジムバルター2017
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.