[C#] Equality

이정석·2024년 1월 31일

CSharp

목록 보기
7/22

Equality

참조 타입과 값 타입의 동등성 비교는 비교할 때 ==를 사용하는지 Equals를 사용하는지에 따라 다르다. 값 타입의 비교는 어느정도 예상할 수 있겠지만 참조 타입의 경우 실제 내부 값을 비교하는지 객체의 주소값을 비교하는지 굉장히 헷갈릴 수 있다고 생각한다.

1. Reference type

아래 코드는 Point 클래스를 정의하고 서로다른 객체들을 ==Equals를 이용하여 비교하였을 때를 비교한 예제코드이다.

  class Point
  {
      private int x = 0;
      private int y = 0;

      public Point(int xPos, int yPos) { x = xPos; y = yPos; }
  }

  class Program
  {
      public static void Main(string[] args)
      {
          Point p1 = new Point(1, 1);
          Point p2 = p1;
          Point p3 = new Point(1, 1);

          // == 연산자
          // 기본 동작: 참조(주소)가 동일한가?
          Console.WriteLine(p1 == p2); // true
          Console.WriteLine(p1 == p3); // false

          // Equals() 사용
          // 기본 동작: 참조(주소)가 동일 한가?
          Console.WriteLine(p1.Equals(p2)); // true
          Console.WriteLine(p1.Equals(p3)); // false
      }
  }

참조 타입에서 비교연산자와 Equals를 이용한 비교의 기본 동작은 동일하다. 같은 곳을 참조하고 있는지를 검사하는데 객체가 할당된 주소를 비교한다고 생각하면 된다.

그렇다면 두 방법에는 어떤 차이가 있을까? Equals를 가상 메소드로 되어있어 상속구조를 고려한다면 Equals를 이용하는 것이 더 유연하게 동작할 수 있다.

공식문서에서 추천하는 방법은 Equals를 이용한 동등성 비교이다.

참조 비교말고 특수한 방법으로 비교를 진행하고 싶다면 연산자 오버로딩과, 메소드 오버라이딩을 사용하면 된다. 아래 코드와 같이 구현한다면 사용자 정의 동등성 비교가 가능하다.

  class Point
  {
      private int x = 0;
      private int y = 0;

      public Point(int xPos, int yPos) { x = xPos; y = yPos; }

      public override bool Equals(object obj)
      {
          Point pt = (Point)obj;
          return x == pt.x && y == pt.y;
      }

      public static bool operator ==(Point pt1, Point pt2)
      {
          return pt1.x == pt2.x && pt1.y == pt2.y;
      }

      public static bool operator !=(Point pt1, Point pt2)
      {
          return !(pt1.x == pt2.x && pt1.y == pt2.y);
      }
  }

두 방법의 차이를 나타내는 코드는 아래와 같다. 모든 클래스는 System.Object를 상속받기 때문에 참조를 object 클래스로 해도 괜찮다. 하지만, 참조를 object로 하였을 때 ==연산자는 사용자 정의 동등성 비교가 적용되지 않는다.

    public static void Main(string[] args)
    {
        //Point p1 = new Point(1, 1);
        //Point p2 = p1;
        //Point p3 = new Point(1, 1);

        object p1 = new Point(1, 1); // == 연산자 적용 X
        object p2 = p1;
        object p3 = new Point(1, 1);
    }

2. Value type

값 타입은 참조 타입과 다르게 ==가 제공되지 않지만 정의를 하면 직접 구현은 가능하다. 제공되지 않는 기능이기 때문에 바로 사용한다면 컴파일에러가 발생한다.

Equals는 기본적으로 적용되지만 참조 타입처럼 메모리 주소를 비교하는 것이 아닌 스택 영역에 할당된 메모리 블록을 비교한다.

  struct Point
  {
      private int x = 0;
      private int y = 0;

      public Point(int xPos, int yPos) { x = xPos; y = yPos; }
  }

  class Program
  {
      public static void Main(string[] args)
      {
          Point p1 = new Point(1, 1);
          Point p2 = p1;

          // == 연산자
          // 기본 제공 X, 재정의 가능
          Console.WriteLine(p1 == p2); // 컴파일 에러

          // Equals() 사용
          // 기본 동작: 메모리 블록 비교, 재정의 가능
          Console.WriteLine(p1.Equals(p2)); // true
      }
  }
profile
게임 개발자가 되고 싶은 한 소?년

0개의 댓글