System.Object를 제대로 구현하는 클래스 만들기

또 다시 긴 시간 동안 공백이 있었군요. 그 사이에는 기말고사 시즌이라 공부에 전념하게 되어 카페를 자주 방문하지는 못했습니다. 아직 이틀이 더 남았지만 힘든 시험은 거의 다 지나가서 잠시 여유를 가지고 강좌를 올립니다. ^^


System.Object 클래스는 닷넷 세계에서 가장 최상위 클래스이며 모든 개체들은 이 클래스로 핸들링하는 것이 가능하다고 말씀드린 적이 있었습니다. System.Object의 모든 멤버를 정확히 구현하면 닷넷에서 가장 사용하기 쉬운 클래스가 됩니다. 그리고 몇 가지 멤버 함수를 더 구현하면서 확장된 기능도 추가할 수 있습니다.


1. 해시 테이블의 ID로 사용할 난수를 생성하기: GetHashCode()


해시 테이블의 정의에 의하면, 해시 테이블의 모든 원소 (Atom)들은 각자의 고유한 Identity를 가지고 있어서 어떤 개체를 무작위로 정확히 검색하는 것이 가능합니다. 이 Identity를 생성하도록 해주는 것이 System.Object.GetHashCode() 라는 함수입니다. 이것을 커스터마이징해서 더욱 더 고유한 Identity를 만들 수 있습니다.



  • Step A. 멤버 변수로 System.Guid 개체를 선언하고 Guid.NewGuid()를 써서 초기화한다.
  • Step B. GetHashCode()를 override 키워드를 써서 재정의한다.
  • Step C. Guid 개체의 GetHashCode를 비롯, 사용하는 멤버 변수에서 모두 GetHashCode()를 호출하여 각 값을 ^ 연산 처리한다.
  • Step D. 나온 값을 반환한다.

위와 같이 구현하였을 경우 Guid (Global Unique Identity)의 특성과 유동적으로 변형되는 각 멤버 함수의 값의 조합에 의하여 고유한 Identity를 가지게 됩니다.


2. 특정 개체의 동등성을 확인하기 위한 함수: Equals()


이 메서드를 직접 호출하지 않는다 하더라도 == 연산자를 씀으로서 이 메서드를 호출하게 됩니다. 진정으로 어떤 개체가 같다는 의미를 부여하기 위하여 Equals() 함수를 직접 구현하여 사용자가 의도하는 대로 비교를 하도록 처리하는 것이 좋습니다.


Equals() 함수는 두 번 써주는 것이 좋습니다. 첫 번째는 System.Object로 부터 override하는 버전, 두 번째는 새롭게 Equals 함수를 정의하는 것으로 합니다.


A. 첫 번째 함수는 다음과 같이 작성한다.


return (obj is [형식명]) ? this.Equals(obj as [형식명]) : base.Equals(obj);


B. 두 번째 함수에서는 각 멤버들을 비교하여 참/거짓 여부를 반환한다. 단, 주의할 것은 1번 섹션의 Guid는 비교 대상에서 빼야 한다는 점이다.


3. 특정 개체에 대한 상세한 정보를 출력하기: ToString()


이 메서드를 구현하지는 않더라도 개략적인 정보 하나 정도는 자동으로 추출됩니다. 현재 클래스의 전체 경로가 나오는데, 기왕이면 이것보다는 특정 개체에 대한 구체적인 정보를 표현하는 것이 더 보기 좋을 것입니다. 이것은 직접 작성하실 수 있을것입니다.


4. 리플렉션이 아닌 특별한 형변환을 가능하게 하기: implicit operator 또는 explicit operator


선언한 클래스에서 해당하는 형식의 멤버가 하나만 존재하고, 이것을 외부에 노출시키기로 결정하고 프로퍼티까지 선언하였다고 가정할 때, 좀 더 편리하게 하기 위하여 implicit operator 또는 explicit operator를 선언하면 프로퍼티를 참조하는 대신 캐스팅 연산자를 쓰거나 혹은 바로 대입하는 것이 가능합니다. (명시적인 캐스팅 연산자를 사용하는 것이 혼란을 줄일 수는 있겠습니다.)



    // [반환할 형식] abcd = [선언한 클래스 형식명] test;


    // 캐스팅 연산자 없이 개체를 특정 형식의 변수에 직접 대입하면 호출됩니다. (암묵적 형변환)


    public static implicit operator [반환할 형식] ([선언한 클래스 형식명] obj)


    {


      return obj.m_test;


    }

    // [반환할 형식] abcd = ([선언한 클래스 형식명])test;


    // 캐스팅 연산자를 써줌으로서 명시적인 형변환을 할 수 있습니다.


    public static explicit operator [반환할 형식] ([선언한 클래스 형식명] obj)


    {


      return obj.m_test2;


    }

    // [선언할 클래스 형식명] value = new 선언할 클래스 형식명;


    // value = [값];


    // 프로퍼티의 Setter를 대신할 수 있습니다.


    public static implicit operator [선언한 클래스 형식명] ([받아들일 형식] obj)


    {


      // return new 선언한 클래스 형식명;


      // // 혹은


      [선언한 클래스 형식명] result = new 선언한 클래스 형식명;


      result.m_test3 = obj;


      return result;


    }


위와 같은 형식으로 객체를 작성하면 “잘 준비된” 형태의 객체를 작성할 수 있습니다.

댓글 남기기