ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 5장. 기본, 참조, 값 타입
    CLR Via C# 2022. 1. 17. 23:48

    프로그래밍 언어의 기본 타입

    기본 타입(Primitive Type) : System.Int32 => int 같이간단히 프레임워크 클래스 라이브러리상에 정의된 타입과 직접 연결돼 간단히 사용할 수 있는 데이터 타입. 기본적으로 제공되는 별칭 선언으로 볼 수도 있다. 이는 사실 언어별 컴파일러마다 다른 부분이 있어서 혼동을 야기하기도 한다. 특히 CLR환경에서 문제이다.


    기본 타입 연산의 오버플로우 여부 검사

    c#의 경우 기본적으로는 오버플로우 검사를 하지않는다. 컴파일러 스위치에서 /checked+를 지정해 오버플로우 여부를 검사할 수 있다. 발생시 System.OverflowException 예외를 방출한다. 또는 checked 와 unchecked를 통해서 블럭 단위로 설정할 수도 있다. 다음은 오버플로우에 관련한 문제를 극복하기 윈한 내용들이다.

    • 부호 없는 데이터 타입 대신 부호 있는 데이터 타입을 사용하는 것을 선호하라
    • 사용자가 입력한 데이터를 사용하는 부분은 검증하는 것이 좋다.
    • 해시 값 계산이나 체크섬 값 계산같이 오버플로우 검사를 하지 않아도 되는 부분unchecked를 명시적으로 사용하라
    • uncheked 키워드를 사용하지 않은 코드에선 오버플로우 검사를 직접하는 것이 좋다.

    Visual studio 에서는 속성메뉴에서 빌드탭고급으로 들어가 산술 연산 오버플로/언더플로 확인 옵션을 변경해 검사하게 할 수 있다.


    참조 타입과 값 타입

    참조타입은 관리되는 힙에 항상 할당된다. 값 타입은 스레드 스택에 할당된다.

    클래스, 인터페이스 = 참조 타입

    구조체, 열거 타입 = 값 타입 (new사용하지 않아도 할당은 됨. 초기화는 안됨.)

    다음의 경우에 해당하면 값 타입으로 선언해야한다.

    • 인스턴스 필드에서 수정할 내용이 없거나 수정할 수 없는 타입
    • 다른 타입으로부터 상속 받을 필요가 없는 타입
    • 다른 타입의 기본 타입으로 쓰이게 할 계획이 없는 경우
    • 인스턴스의 전체 크기가 약 16바이트 이내로 매우 적은 경우
    • 인스턴스의 크기가 16바이트보다 크지만 메서드의 매개변수나 반환 타입으로 쓰이는 일이 없음을 확신할 수 있는 경우
    [System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Auto)]
    internal struct SomeValtype{
        ...
    }        // 값 타입에 대한 필드 레이아웃을 CLR이 관리하도록 할 수 있다.
            // LayoutKind.Explicit을 지정하고 명시적으로 FieldOffset을 지정해 공용체 구현 가능

    박싱된 값 타입과 박싱되지 않은 값 타입

    매개변수로 객체를 받는 메서드에 값 타입을 넣으면 값 타입에서 참조 타입으로의 타입 변환이 일어난다. 이를 박싱이라고 한다. 박싱이 일어나면 관리되는 힙에 메모리가 할당되고 거기에 값 타입의 필드들이 복사된다. 그 후 이 객체의 메모리 주소를 반환한다. 언박싱이란 박싱된 객체의 필드들의 주소를 가져오는 것이다. 보통 이 메모리로부 값들을 복사하는 작업을 한다. 이런 박싱을 최소화 하려면 값 타입의 매개변수로 받는 메소드를 오버로딩하는 것이다.


    객체의 동일함과 식별

    System.Object 타입은 Equals 가상 메서드를 제공한다. 하지만 기본 동작은 단지 동일한 참조인지를 판단할 뿐이다. 그래서 Equals을 재정의 하려면 올바른 구현은 다음과 같다.

    1. obj 매개변수가 null로 전달되면 false 반환
    2. this와 obj매개변수가 가리키는 대상이 동일하면 true (여기까지가 기본 구현)
    3. this와 obj가 가리키는 대상의 타입이 전혀 다른 경우 false
    4. 타입 내에 선언된 각 인스턴스 필드에 대해, 하나라도 일치하지 않으면 false를 반환
    5. 상위 클래스 타입에 대해 Equals 메서드를 호출하여 상위 클래스 타입에 선언된 남은 필드들 비교해 false나오면 fasle를 true가 나오면 true 최종적으로 반환한다.

    객체 해시 코드

    만약 Equals 메소드를 재정의했다면, GetHashCode 메소드도 재정의 해야한다. System.Collection.HashTable을 비롯한 모든 컬렉션들은 두 개의 객체가 동일한 객체인지 확인하기 위하여 해시 코드를 계산해 같은지 알아본다. 그래서 Equals이 재정의되면 GetHashCode또한 일치하는 결과를 낼 수 있도록 재정의 되어야한다.


    dynamic 기본 타입

    리플렉션이나 다른 구성요소와 통신하는 일을 좀 더 쉽게 할 수 있도록, C# 컴파일러는 표현식의 타입을 dynamic 타입으로 만드는 기능을 제공한다. dynamic으로 표시된 표현식이나 변수상의 멤버를 호출하는 코드를 작성하면, 컴파일러는 특별한 IL코드를 만든다. 이 특별한 코드를 페이로드(Payload)라고 한다. 페이로드 코드는 실행 시점에서 dynamic 표현식과 변수에 의하여 참조되는 실제 타입을 기반으로 정확한 연산을 찾아 실행하는 일을 한다. dynamic 타입은 우선 System.Object으로 치환된다. 그리고 메타데이터 상에 흔적을 남긴다.(지역변수는 메타데이터에 흔적을 남기지는 않아 그저 System.Object과 동일할 뿐이다.) 그 후, Object타입이 다른 타입으로 암묵적으로 캐스팅된다. 원래는 불가능하지만 dynamic 타입에서의 변환은 허용한다.

    var와 dynamic은 비슷해 보이는 기능을 한다. 하지만 var는 지역변수에만 쓰이고, dynamic은 지역 변수 외에도 필드, 매개변수에도 사용할 수 있다. 캐스팅도 가능하다. 초기값도 필요없다.

Designed by Tistory.