web-dev-qa-db-ja.com

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

これらの3つの私の理解は:

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

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

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

しかし C#ステーション 状態:

オブジェクトクラスでは、EqualsメソッドとReferenceEqualsメソッドは意味的に同等ですが、ReferenceEqualsはオブジェクトインスタンスでのみ機能します。 ReferenceEqualsメソッドは静的です。

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

73
999999

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


あなたはそれぞれの意味の違いについて大雑把に正しいです(「同じオブジェクトの異なるインスタンス」は少し混乱しているようですが、おそらく「同じtype)およびこれについてオーバーライドできます。

それを別として、質問の最後の部分、つまり、plainSystem.ObjectinstancesとSystem.Objectreferencesがどのように機能するかを処理しましょう(==の非多態性を回避するには両方が必要です) 。ここでは、3つの操作すべてが同等に機能しますが、警告:Equalsをon nullで呼び出すことはできません。

Equalsは、oneパラメーター(whichcanbe null)。インスタンスメソッドであるため(実際のオブジェクトで呼び出す必要があります)、null- referenceで呼び出すことはできません。

ReferenceEqualsは、twoパラメーターを使用する静的メソッドで、いずれか/両方ともnullにすることができます。オブジェクトは静的(オブジェクトインスタンスに関連付けられていない)であるため、どのような状況でもa NullReferenceExceptionをスローしません。

==は演算子です。この場合(object)は、ReferenceEqualsと同じように動作します。 a NullReferenceExceptioneitherはスローしません。

説明する:

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
71
Ani

このMSDN記事 をご覧ください。

適切なポイントは次のとおりです。

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

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

お役に立てれば!

18
Alastair Pitts

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

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

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

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

6
Luke Schafer

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

  1. ReferenceEquals(object、object)は、「(object)arg1 == arg2」と同じです(値型の場合、ボクシングが発生し、時間がかかります)。ただし、このメソッドは、次のようないくつかの状況で引数のnullをチェックする唯一の100%安全な方法です。

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

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

3
sotonika

Objectクラスでは、.Equalsは等式ではなくアイデンティティを実装します。参照が等しいかどうかを確認します。コードは次のようになります。

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

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

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

何らかのクラスを使用する場合、logically .Equalsは同等性をチェックし、.ReferenceEqualsは同一性をチェックします。

1
Yola

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);
    }
}
1
JohnLBevan