프로그래밍 언어/C#

C# Equals, ==, ReferenceEquals 비교

ShovelingLife 2023. 6. 14. 13:03

1-1) 같음 연산자 '=='

연산자 왼쪽 오른쪽에 오는 것들이 같은지를 판단하는 연산자다
값 타입의 경우 좌 우의 값이 같으면 True, 다르면 False 를 반환한다.
참조 타입은 좌 우 객체가 같은지를 비교한다. 
(객체 내부의 값이 같은지 비교 한다는 뜻, 참조(주소)는 상관없음)

1-2) Equals 도 똑같이 == 과 동일한 역할

public virtual bool Equals(object? obj)
public virtual bool Equals(object? objA, object? objB)

Equals 메서드는 기본적으로 객체가 같은지를 비교한다. 기본적으로는 == 과 동일한 작동한다.
값 타입은 값이 같은지 비교를 하고 참조 타입은 객체가 같은지를 비교한다.
(객체 내부의 값이 같은지 비교 한다는 뜻, 참조(주소)는 상관없음)
object 클래스의 Equals 를 보면 내부적으로 == 으로 비교하고 있는 것을 알 수 있다.

C#에서 기본적으로 정의된 Equals 는 == 과 동일한 작동을 한다. 하지만 Equals를 보면 virtual 키워드가 있는것을 알 수 있다. 이것은 사용자(프로그래머)가 언제 어디서는 해당 메서드를 재정의(override)해서 사용할 수 있다는 뜻이다. 그렇기 때문에 "Equals 메서드는 기본적으로 == 과 동일한 역할을 하지만, 재정의에 따라서 어떤 동작을 할지 확신할 수는 없다."

1-3) ReferenceEqual 메서드

public static bool ReferenceEquals(object? objA, object? objB)

첫번째 두번째 매개변수로는 비교하고자 하는 객체를 넣는다. 두 객체의 참조가 같으면 True, 다르면 False 를 반환한다.
ReferenceEquals는 진짜 해당 참조를 비교하는 메서드 (주소 비교)
Equals는 클래스 등에서 사용할때 Equals 를 각 클래스에 맞게 오버라이딩 해서 사용해야 하지만 ReferenceEquals는 오버라이딩 할 수 없다. 그러니, 참조가 같은지 (주소가 같은지)를 비교할때는 이 ReferenceEquals를 사용하면 된다.
ReferenceEquals는 "참조(가리키는 주소)"가 같은지 확인하는 것 이기 때문에 값타입 비교는 항상 False 가 나오게 된다.

2-1) 값 타입

using System;

public static void Main(string[] args)
{
    int a = 10;
    int b = 10;
    int c = a;

    // a, b, c 모두 "BlockDMask" 문자열을 가지고 있지만.
    // a, c 가 같은 객체를 가리키고 있고(참조가 같음)
    // b만 다른 객체를 가리키고 있다.(참조가 다름)
    Console.WriteLine($"a : {a}, {a.GetTypeCode()}");
    Console.WriteLine($"b : {b}, {b.GetTypeCode()}");
    Console.WriteLine($"c : {c}, {c.GetTypeCode()}");

    // == 비교 (객체 내부 값이 같은지)
    Console.WriteLine("\n1. == 비교");
    Console.WriteLine($"a == b : {a == b}");
    Console.WriteLine($"b == c : {b == c}");
    Console.WriteLine($"c == a : {c == a}");


    // Equals 비교 (override에 따라 다르지만, string 은 객체 내부 값 같은지)
    Console.WriteLine("\n2. Equals 비교");
    Console.WriteLine($"a.Equals(b) : {a.Equals(b)}");
    Console.WriteLine($"b.Equals(c) : {b.Equals(c)}");
    Console.WriteLine($"c.Equals(a) : {c.Equals(a)}");


    // ReferenceEquals 비교 (참조가 같은지, 값 타입에서는 의미 없음...... 쓰지말것)
    Console.WriteLine("\n3. ReferenceEquals 비교");
    Console.WriteLine($"object.ReferenceEqual(a, b) : {ReferenceEquals(a, b)}");
    Console.WriteLine($"object.ReferenceEqual(b, c) : {ReferenceEquals(b, c)}");
    Console.WriteLine($"object.ReferenceEqual(c, a) : {ReferenceEquals(c, a)}");
}

값 타입을 보면 == , Equals 둘다 동일하게 그리고 당연하게 값을 비교 하게 된다. 반면 ReferenceEquals 에서는 모두 False 가 나오게 된다. 값 타입에서는 ReferenceEquals는 무의미한 메서드다.

2-2) 참조 타입

using System;

public static void Main(string[] args)
{
    string a = "BlockDMask";     
    string b = "Block";
    b += "DMask";
    string c = a;   // c와 a 는 같은 참조

    // a, b, c 모두 "BlockDMask" 문자열을 가지고 있지만.
    // a, c 가 같은 객체를 가리키고 있고(참조가 같음)
    // b만 다른 객체를 가리키고 있다.(참조가 다름)
    Console.WriteLine($"a : {a}, {a.GetTypeCode()}");
    Console.WriteLine($"b : {b}, {b.GetTypeCode()}");
    Console.WriteLine($"c : {c}, {c.GetTypeCode()}");

    // == 비교 (객체 내부 값이 같은지)
    Console.WriteLine("\n1. == 비교");
    Console.WriteLine($"a == b : {a == b}");
    Console.WriteLine($"b == c : {b == c}");
    Console.WriteLine($"c == a : {c == a}");


    // Equals 비교 (override에 따라 다르지만, string 은 객체 내부 값 같은지)
    Console.WriteLine("\n2. Equals 비교");
    Console.WriteLine($"a.Equals(b) : {a.Equals(b)}");
    Console.WriteLine($"b.Equals(c) : {b.Equals(c)}");
    Console.WriteLine($"c.Equals(a) : {c.Equals(a)}");


    // ReferenceEquals 비교 (참조가 같은지)
    Console.WriteLine("\n3. ReferenceEquals 비교");
    Console.WriteLine($"object.ReferenceEqual(a, b) : {ReferenceEquals(a, b)}");
    Console.WriteLine($"object.ReferenceEqual(b, c) : {ReferenceEquals(b, c)}");
    Console.WriteLine($"object.ReferenceEqual(c, a) : {ReferenceEquals(c, a)}");
}

a, b, c 문자열을 각각 생성해봤는데 a와 c는 참조도 같고 값도 같은 문자열이고 b는 참조가 a,c랑 다르지만, 값은 같은 문자열이다. (b를 저렇게 선언한 이유는 C# 단에서"BlockDMask"를 동일하게 선언하는 경우 최적화를 통해서 동일한 참조를 가리킬 수 있기 때문이다.

 

출처 : https://blockdmask.tistory.com/602