programing tip

C # .Equals (), .ReferenceEquals () 및 == 연산자

itbloger 2020. 10. 22. 07:46
반응형

C # .Equals (), .ReferenceEquals () 및 == 연산자


이 세 가지에 대한 나의 이해는 다음과 같습니다.

  • .Equals()데이터 동등성 테스트 (더 나은 설명이없는 경우) .Equals()동일한 객체의 다른 인스턴스에 대해 True를 반환 할 수 있으며 이것은 가장 일반적으로 재정의되는 메서드입니다.

  • .ReferenceEquals() 두 개체가 동일한 인스턴스이고 재정의 할 수 없는지 여부를 테스트합니다.

  • ==ReferenceEquals()기본적으로 와 동일 하지만 이것은 무시할 수 있습니다.

그러나 C # 스테이션 은 다음과 같이 말합니다.

개체 클래스에서 EqualsReferenceEquals메서드는 ReferenceEquals개체 인스턴스에서만 작동 한다는 점을 제외하면 의미 상 동일 합니다. ReferenceEquals방법은 정적이다.

이제 이해가 안 돼요. 누구든지 이것에 대해 밝힐 수 있습니까?


혼란의 원인은 "... Equals 가 개체 인스턴스에서만 작동 한다는 점을 제외하면 C # 스테이션에서 추출한 내용에 오타가있는 것 같습니다 . ReferenceEquals 메서드는 정적입니다."


각각의 의미 론적 의미의 차이에 대해 느슨하게 맞습니다 ( "동일한 객체의 다른 인스턴스"가 약간 혼란스러워 보이지만 "동일 유형 의 다른 인스턴스"로 읽어야 함 ).

우리가 그것을 제쳐두면, 당신의 질문의 마지막 부분, 즉 그것들이 일반 System.Object인스턴스와 System.Object참조로 어떻게 작동 하는지를 다루도록합시다 (우리는 둘 다의 비-다형성을 피하기 위해 필요합니다 ==). 여기서 세 가지 작업은 모두 동일 하게 작동 하지만주의 사항 :에서 Equals호출 할 수 없습니다 null.

Equals하나의 매개 변수 (일 있음 null)를 사용 하는 인스턴스 메소드입니다 . 인스턴스 메서드 (실제 객체에서 호출되어야 함)이므로 null-reference에서 호출 할 수 없습니다 .

ReferenceEquals두 개의 매개 변수 를 취하는 정적 메소드입니다 null. 둘 중 하나는 . 정적이기 때문에 (object instance 와 연결되지 않음 ) 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

주제에 대한 이 MSDN 문서살펴보십시오 .

관련 포인트는 다음과 같습니다.

참조 동등성을 확인하려면 ReferenceEquals를 사용하십시오. 값이 같은지 확인하려면 Equals 또는 Equals를 사용하십시오.

기본적으로 == 연산자는 두 참조가 동일한 객체를 나타내는 지 확인하여 참조 동등성을 테스트하므로 참조 유형은이 기능을 얻기 위해 == 연산자를 구현할 필요가 없습니다. 유형이 변경 불가능한 경우 (즉, 인스턴스에 포함 된 데이터를 변경할 수 없음), 참조 같음 대신 값 같음을 비교하기 위해 연산자 ==를 오버로딩하는 것이 유용 할 수 있습니다. 같은 가치.

도움이 되었기를 바랍니다!


.ReferenceEquals에 대한 이해가 정확합니다.

.Equals는 값 유형에 대한 데이터 동등성을 확인하고 값이 아닌 유형 (일반 개체)에 대한 참조 동등성을 확인합니다.

객체가 어떤 형태의 데이터 동등성 검사를 수행하도록 Equals를 재정의 할 수 있습니다.

편집 : 또한 .ReferenceEquals는 값 유형에 사용할 수 없습니다 (잘 할 수 있지만 항상 거짓입니다)


"null"과 비교하는 것에 대해 5 센트를 추가하고 싶습니다.

  1. ReferenceEquals (객체, 객체)는 "(객체) arg1 == arg2"와 동일합니다 (따라서 값 유형의 경우 boxing이 발생하고 시간이 걸립니다). 그러나이 방법은 다음과 같은 여러 상황에서 null 인수를 확인하는 유일한 100 % 안전한 방법입니다.

    • a)를 통해 회원에게 전화하기 전에. 운영자
    • b) AS 연산자의 결과 확인.
  2. == 및 Equals (). ReferenceEquals가 null 검사를 통해 100 % 안전하다고 말하는 이유는 무엇입니까? 핵심 교차 프로젝트 라이브러리에 일반 확장을 작성하고 일반 매개 변수 유형을 일부 도메인 유형으로 제한한다고 가정 해보십시오. 이 유형은 "=="연산자를 도입 할 수 있습니다-지금 또는 나중에 (그리고 저를 믿습니다. 저는이 연산자가 매우 "이상한"논리를 가질 수 있습니다. 특히 도메인이나 지속성 객체에 관해서는 그렇습니다). null 인수를 확인한 다음 멤버 작업을 호출하려고합니다. 놀랍게도 여기서 NullRef를 가질 수 있습니다. == 연산자는 Equals ()와 거의 동일하기 때문에 매우 사용자 정의되고 예측할 수 없습니다. 하지만 고려해야 할 차이점이 있습니다. 일반 매개 변수를 일부 사용자 정의 유형으로 제한하지 않으면 (== 유형이 "클래스"인 경우에만 사용할 수 있음) == 연산자는 객체와 동일합니다. . ReferenceEquals (..). Equals 구현은 가상이므로 항상 최종 유형에서 사용됩니다.

따라서 내 권장 사항은 자신의 유형을 작성하거나 잘 알려진 유형에서 파생 될 때 ==를 사용하여 null을 확인할 수 있다는 것입니다. 그렇지 않으면 object.ReferenceEquals (arg, null)를 사용합니다.


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를 확인합니다.


Ani의 훌륭한 답변확장 하여 참조 유형 및 재정의 된 같음 방법을 다룰 때 주요 차이점을 보여줍니다.

.

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

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

참고 URL : https://stackoverflow.com/questions/3869601/c-sharp-equals-referenceequals-and-operator

반응형