CS/Effective C#

const보다는 readonly가 좋다.

Mirab 2023. 4. 23. 03:07

간혹 성능을 위해서 상수형을 작성하게 될 때 엄청난 고민을 하게 됩니다.

이때 누구는 const를 쓰고, 또 누구는 readonly를 쓰고 있습니다. 어떨 때 이걸 쓰고, 또 어떨 때 이걸 쓰는지 코드를 작성하다 보면 고민이 깊어져 2시간가량을 먹었던 기억이 있습니다.

그 답 오늘 해결해봅시다!

 

C#은 컴파일타임 상수와 런타임 상수 2 유형의 상수를 가집니다.

컴파일타임 상수는 const를 의미하고, 런타임 상수는 readonly를 의미합니다.

// 컴파일타임 상수
public cosnt int Millennium = 2000;

// 런타임 상수
public static readonly int ThisYear = 2004;

컴파일타임 상수가 약간 더 빠르긴 하지만, 런타임 상수에 비해서 유연성이 떨어집니다.

결론적으로 말하자면,

컴파일타임 상수는 성능이 매우 중요하고 상수의 값이 절대로 바뀌지 않는 경우에만 제한적으로 사용하고,

나머지는 런타임 상수를 사용하는 것을 추천합니다.

 

컴파일타임 상수는 메서드 내에서 선언이 가능, 런타임 상수는 메서드 내에서 선언이 불가능

서로 다르게 동작하는 이유는 값에 접근하는 방식이 서로 다릅니다.

컴파일타임 상수는 컴파일타임에 변수가 값으로 대체됩니다.

// 사용자가 작성한 코드
if (myDateTime.Year == Millenium)

// IL 코드로 컴파일되면 아래와 같다.
if (myDateTime.Year == 2000)

반면에 런타임 상수런타임에 값이 평가됩니다.

상수처럼 값으로 대체되지 않고 상수에 대한 참조로 컴파일됩니다. (한 번 살펴보고 그 값으로 컴파일된다는 뜻)

 

이러한 차이로 인해서

컴파일 상수는 내장된 숫자형, enum, 문자열, null에 대해서만 사용이 가능합니다.

오히려 그 외의 값을 사용하려고 할 때에는 컴파일 오류가 발생합니다.

private const DateTime localTime = new DateTime(2023, 4, 23, 0, 0, 0);

런타임 상수는 생성자에서 초기화될 수 있으며 그 이후에는 수정될 수 없습니다.

런타임은 컴파일 상수보다 아까 유연하게 사용될 수 있다고 했습니다.

해당 런타임 상수는 어떤 타입과도 함께 사용이 가능하며, 멤버 초기화 구문뿐 아니라 생성자를 통해서도 초기화가 가능하다는 점입니다. (서로 같은 클래스의 인스턴스지만 내부적으로는 생성자에 의해 서로 다른 값을 가지는 장점도)

 

클래스 내에서 런타임 상수를 정의하는 경우라면 동일 클래스의 인스턴스라 하더라도 인스턴스 별로 서로 다른 값을 가질 수 있습니다. 반면에 컴파일타임 상수는 정의에 따라 정적 상수이므로 모든 인스턴스가 동일한 값을 가질 수밖에 없습니다.

 

가장 중요한 차이

상수의 값이 런타임에 평가된다는 점입니다.

런타임 상수를 참조하는 코드를 컴파일하면 컴파일타임 상수처럼 코드를 값으로 대체하지 않고, readonly 변수에 대한 참조 코드를 생성합니다.

 

응용프로그램을 유지보수할 때 상당한 영향을 미치는데요.

컴파일타임 상수는 다른 어셈블리의 참조 여부와 상관없이 항상 숫자나 문자열 등을 직접 사용한 것과 완전히 동일한 IL 코드를 생성합니다.

물론 성능상 좋겠지만 응용프로그램 전체를 리빌드(rebuild) 하지 않고 수정된 부분만 빌드(build)하면 오히려 예상했던 것과 다르게 이전에 빌드했는 결과가 나올 수 있다는 것입니다.

 

응용프로그램의 크기가 작으면 상관없겠지만, 보통은 리빌드 하는데 30 ~ 1시간 (많은 시간)이 소요되기 때문에 내가 수정한 영역만 다시 빌드하는 Build를 하기 마련입니다. 그런데 빌드만 하기에는 컴파일타임 상수는 수정된 것으로 반영되지 않는다는 것이죠. 그래서 해당 상수를 참조하는 모든 코드를 반드시 재컴파일 해야 합니다.

 

반대로 런타임 상수는 이러한 대응에 유연합니다. readonly 변수에 대한 참조 코드를 생성했기 때문에 컴파일 시에 참조 코드가 생성되고 런타임이 비로소 그 값을 평가하게 됩니다.

이렇게만 보면 readonly가 최고?!

readonly 대신 const를 사용했을 때 얻을 수 있는 장점은 성능이 빠릅니다.

코드를 상수값으로 대체하기 때문에 readonly 변수를 통해서 값을 참조하는 것보다는 당연히 빠를 수밖에 없습니다.

하지만,, 성능 개선 효과가 크지 않고 유연성을 해치기 때문에 사용자의 판단이 무엇보다 중요합니다.

 

특성 매개변수(attribute), swich/case 문의 레이블, enum 정의 시 사용하는 상수 등은 컴파일 시에 사용돼야 하므로 반드시 const 통해서 초기화하고 그 외에는 readonly를 사용하는 것이 좋습니다.