본문 바로가기
C#

CLR : System.Object

by JsonOnTheXaml 2021. 3. 20.

모든 타입은 System.Object 로부터 파생된다.

결과는 동일하다.

따라서, 모든 개체의 타입은 다음과 같은 Public Instance method를 반드시 포함한다.

  • Equals
    • 두 개의 개체가 같은 값을 가지고 있으면 true를 반환한다.
    • 참조 형식의 경우 같은 메모리 주소를 가리킬 때, 값 형식의 경우 모든 멤버의 값이 같을 때.
  • GetHashCode
    • 개체 값에 대한 해시 코드를 반환한다.
    • 개체가 해시 테이블에서 키를 사용하려 한다면 이 메서드를 override 해야 한다.
  • ToString
    • 기본적으로 타입의 전체 이름을 반환한다(this.GetType().FullName).
    • 대부분은 이 메서드를 재정의하여 개체의 상태를 표현하는 문자열 개체를 반환하도록 사용한다.
    • 디버깅을 위해 개체의 값을 문자열로 반환하기도 한다.
  • GetType
    • 개체의 유형을 식별하기 위해 이를 호출하면, 개체의 타입을 가리키는 Type을 상속받은 타입의 인스턴스를반환한다.
    • 반환하는 타입 개체는 타입에 대한 메타데이터 정보를 가진 Reflection 클래스에서 사용할 수 있다.
    • 가상 메서드가 아니므로 override 가 불가능하다. 이는 타입 안정성을 위한 것이다.

추가적으로 Protected method 가 제공된다.

  • MemberwiseClone
    • 가상 메서드가 아님.
    • 타입에 대한 새로운 인스턴스를 생성하고, 새로운 개체 인스턴스 필드의 상태를 기존 개체 인스턴스의 필드 상태와 동일하게 설정하여 이렇게 생성된 인스턴스에 대한 참조가 반환된다.
  • Finalized
    • 가상 메서드
    • GC가 개체에 할당된 메모리를 수집하기 전에 호출된다.
    • 리소스의 해지가 필요한 경우, 이 메서드를 override 해서 자원을 정리해야 한다.

CLR 에서는 new 명령을 이용하여 개체를 생성해야 한다. 이 명령은 다음에 해당하는 작업을 모두 수행하고 나면 새롭게 생성된 개체의 참조를 반환한다.

  • 모든 필드에 대하여 인스턴스 인스턴스가 필요로 하는 모든 바이트 수를 계산한다.
    • 힙 메모리에 로드되는 모든 개체는 추가적인 멤버가 필요하다.
    • Type object pointer(타입 개체 포인터) 와 Sync block index(동기화 블록 인덱스)가 CLR에 의하여 관리되기 위해서 필요하다.
    • 따라서 개체의 바이트 크기가 약간 증가한다.
  • 타입에 필요한 바이트의 메모리를 관리되는 힙에서 개체로 할당한다. 그 다음 모든 바이트를 0으로 초기화한다.
  • 개체의 Type object pointerSync block index 멤버를 초기화한다.
  • new를 호출함으로써 타입의 인스턴스 생성자가 호출되고, 생성자를 통해서 지정된 인자("param")가 전달된다.
  • 대부분의 컴파일러는 생성자 내부에서 부모 클래스의 생성자를 호출하는 코드를 자동 생성한다.
    • 따라서, 각각의 생서자는 호출되는 생성자에 의해 타입에 정의된 인스턴스의 필드를 초기화해야 한다.
    • 결론적으로 System.Object 생성자가 호출되지만 특별한 작업을 하지 않고 바로 반환된다.
      • ILDasm.exe 툴을 이용하여 MSCorLib.dll 을 로드하여 쉽게 확인할 수 있다.

타입들 사이의 변환(형 변환)

CLR의 가장 중요한 기능 중 하나는 타입 안정성이다. 런타임 중에 CLR은 개체의 타입이 무엇인지 항상 알고 있다. 개발자는 GetType 를 통해 알 수 있다.

CLR은 개체를 자신의 타입이나 자신의 상위 타입으로 변환하는 것을 허용한다.

  • C#의 경우, 개체의 상위 타입으로 변환하는데 특별한 문법을 필요로 하지 않는다. 기본 타입으로의 변환은 안전하고 암시적인 변환으로 고려되기 때문이다.
  • 특정 개체를 하위의 구체적인 타입으로 형 변환하는 것은 실행 가능하나 애플리케이션에서는 곧 런타임 오류로 표현된다. 따라서, 이런 경우는 명시적인 변환이 필요하다.

"모든 사람은 동물이다."라는 참인 명제가 있을 때, "모든 동물은 사람이다." 라는 명제는 거짓이다. 사람과 동물의 관계를 생각해보자.

런타임에 CLR은 개체의 실제 타입 또는 상위 타입으로의 변환이 항상 올바르게 수행되는지 변환 명령을 검사한다. 다음의 코드는 컴파일은 가능하나 런타임에 InvalidCastException 예외를 발생시킨다.

DataTime 또한 System.Object 로부터 파생되었기 때문에 컴파일러는 아무 문제없이 컴파일을 해준다.

만일 CLR이 이러한 변환을 허용한다면 타입 안정성은 보장할 수 없고 결과적으로 애플리케이션이 비정상적으로 동작할 수 있다. 또한, 다른 종류의 타입으로 쉽게 위장(Spoof)할 수 있는 보안 위반의 가능성도 있으며, 이로 인해 애플리케이션이 예상하지 못한 결과를 가져올 수 있다. CLR에서 타입 안정성을 보장하는 것은 매우 중요한 기능 중 하나이다.

 

그리고 PromoteEmployee 메서드를 좀 더 적절히 선언하려면 System.Object 대신에 Employee 타입을 인자로 정의하여 컴파일 시점에 컴파일러가 오류를 찾을 수 있도록 하는 것이 좋다.

 

출처 : CLR via C# <2nd Edition> : [Chapter 4. 타입의 기본] : 168p ~ 174p

'C#' 카테고리의 다른 글

CLR : 참조 형식, 값 형식  (0) 2021.03.18