자동으로 static 이기 때문에 코드에서 명시적으로 static 를 지정할 수 없습니다
public static const int X = 1; //컴파일 오류 발생
참조 형식 상수 선언
참조 타입은 const 일 수 없습니다. 다만 string 객체는 가능합니다
엄밀히 말하면 참조 타입이 const일 수 없다기 보다는 const 로 선언된 참조 타입의 값은 null 일수 밖에 없다는 것입니다.
동적으로 메모리에 할당되는 객체와 같은 참조타입을 const 로 선언하여 초기화 할 수 없습니다
예를 들어 다음과 같이 배열(참조타입)을 const로 선언하여 초기화 할 수 없습니다
public const int[] array = new int[] { 1, 2 }; //컴파일 오류 발생
이는 const 상수의 값은 반드시 컴파일 시점에 결정되어야 하는, 다시 말해 컴파일 타임에 완전하게 계산될 수 있는 값이어야 하기 때문입니다. 컴파일러는 const 상수를 컴파일 시 그 값을 어셈블리의 메타데이터에 바로 기록하도록 합니다
그러나 한가지 예외가 있는데요. 바로 문자열 객체입니다.
문자열(string) 역시 참조타입 이지만 const 로 선언할 수 있습니다
문자열은 컴파일 시 그 값을 메타데이터에 정확히 기록할 수 있기 때문에 상수로 사용이 가능합니다
public const string S = "MKEX"; //사용 가능함
더불어 참조 형식을 const 로 선언할 경우 null로 초기화는 가능합니다
public const MyClass myClass = null; //사용 가능함
그러나 상수는 이후에 변경이 불가능하기 때문에 이러한 코드는 무의미 할 수 있습니다
따라서 참조타입 상수화는 문자열을 제외하고는 잘 사용하지 않습니다
const 상수는 참조형태로 전달할 수 없습니다
앞의 내용과 연관되는 부분인데요
const 상수는 ref 나 out 키워드를 사용하여 참조형태로 값을 전달할 수 없습니다
앞서 설명한대로 const 상수는 컴파일 시 메타데이터에 그 값이 직접 기록되기 때문에 런타임 시에 이 상수에 대한
메모리 할당은 이루어지지 않습니다. 따라서 주소 값을 기반으로 하는 ref와 같은 참조형태로 값을 전달할 수 없는 것입니다
상수를 선언하고 그 상수를 사용할 경우 IL 코드를 보겠습니다
다음과 같이 const 상수 X가 10으로 초기화 되어 있고…
class MyClass{
public const int X = 10;
}
이 상수 X를 사용하는 다음 어셈블리의 IL코드를 보면 아래 그림과 같습니다
class Program {
static void Main(string[] args){
Console.WriteLine(MyClass.X);
}
}

Main 메서드의 메타데이터인데요,
상수 X의 실제 값인 10 이 메타데이터로 바로 기록된 것을 확인할 수 있습니다
상수 선언에 다른 상수를 사용할 수 있습니다
다음 코드와 같이 상수를 선언할 때 이미 정의된 다른 상수를 대입하여 상수식을 만들 수 있습니다
class MyClass{
public const int X = 1;
public const int Y = X * 2;
}
readonly
닷넷은 상수 선언을 위해 const 외에 readonly 라는 키워드도 추가로 제공합니다
상수면 상수지 왜 두 가지 버전의 상수를 선언하도록 하는 것일까요?
그것은 바로 상수 값의 초기화 방법에 대한 확장을 주기 위함입니다
앞서 const 는 선언과 동시에 값을 할당, 즉 초기화 해야 한다고 했습니다
그러나 readonly는 여기에 더불어 생성자에서 한번 더 값을 할당할 수 있는 확장성을 제공합니다
즉 readonly 상수는 선언할 때 값을 할당할 수도 있으며 생성자에도 할당할 수 있습니다
다음코드처럼 readonly 상수인 X는 선언 시 초기화 하고 더불어 생성자에서도 그 값을 변경할 수 있습니다
class MyClass{
public readonly int X = 1;
public MyClass(int i){
X = i; //생성자를 통해 상수 읽기전용 X의 값을 할당한다
}
}
결과적으로 readonly 상수의 값을 생성자에 따라 다르게 구현할 수 있는 확장성이 제공되는 것입니다
그래서 const 를 컴파일 타임 상수라고 하며 readonly 를 런타임 상수라고 부르기도 합니다
readonly 상수의 특징을 하나씩 살펴 보도로 하겠습니다
readonly 상수는 선언 시 값을 할당하지 않아도 됩니다
const 상수는 선언과 동시에 값을 초기화해야 하지만 readonly 상수는 선언 시 초기화가 의무는 아닙니다
즉 readonly 상수를 선언할 때 값을 할당하지 않으면 클래스 멤버변수와 같이 타입에 맞는 기본 값이 자동 할당됩니다
readonly 상수는 생성자에서 한번 더 그 값을 변경할 수 있습니다
const 는 선언 시 할당된 값은 이후 변경이 불가능한 반면 readonly 상수는 선언 시 값을 초기화 했다고 하더라도
생성자에서 한번 더 그 값을 변경할 수 있습니다. 이것이 const 와의 핵심적인 차이라 하겠습니다
readonly 상수는 static 이 아닙니다
const 는 자동으로 static 이지만 readonly 는 그렇지 않습니다.
const 가 클래스 상수라면 readonly 는 객체 상수라고 할 수 있습니다.
즉 readonly 상수는 클래스의 인스턴스로 생성된 객체를 통해서 접근할 수 있습니다.
아래 IL코드를 보면 readonly 로 선언된 상수 X는 static 아 아님을 알 수 있습니다

다만 readonly 상수를 정적 상수로 만들고 싶으면 static 키워드를 명시해 주면 됩니다
만일 readonly 를 static키워드와 같이 사용하여 정적 상수로 만들 경우에는 일반 생성자가 아닌
정적 생성자를 통해서만 값을 변경할 수 있습니다
다음 코드를 보면 X 가 정적 상수로 선언되어 있습니다
그리고 정적 생성자를 통해 그 값을 한번 더 변경하도록 합니다
class MyClass{
public static readonly int X = 1;
static MyClass()
{
X = 2; //static로 선언된 readonly상수는 static 생성자를 통해 값을 변경할 수 있다다
}
}
참조 형식 상수 선언
const 에서는 문자열(string)과 null을 제외하고는 참조 타입을 초기화 할 수 없는
반면 readonly는 참조타입의 객체를 생성할 수 있습니다. 즉 다음과 같은 선언이 가능합니다
public readonly int[] array = new int[] { 1, 2 };
따라서 readonly는 상수라는 표현보다는 읽기전용 필드라고 표현하는 것이 일반적입니다
한가지 예외, 리플렉션
readonly 로 선언된 읽기전용 필드는 선언 시 한번 그리고 생성자에서 한번 해서 총 두 지점에서만
값의 초기화가 가능하다고 하였습니다.
그러나 리플렉션 기법을 이용하면 실행 중에도 이 값을 변경할 수 있습니다.이것은 예외 적인 상황이라 할 수 있는데요. 사실 리플렉션은 일반적인 클래스 사용 패턴이 아니기 때문에 굳이 예외적이라기 보다는 특수한 형태라는 표현이 더 맞겠네요. 리플렉션에 관해서는 다음 기회에 알아 보도록 하겠습니다
const 보다는 readonly를 사용하라???
닷넷 개발자들이 자주 하는 말입니다. ‘상수를 사용할 거면 const 보다는 readonly를 사용하라’ 입니다
이것은 다중 어셈블리환경, 어셈블리 간 참조로 구성된 프로그램일 경우 어셈블리 버전 문제에 해당되는 내용입니다
만일 A라는 어셈블리에 const 상수가 존재하고 B라는 어셈블리에서 A를 참조해서 상수를 사용하는 경우에
상수 값이 변경되어 A 어셈블리를 다시 컴파일 해서 배포할 경우 B 어셈블리에서는 이 내용이 갱신되지 않는
문제가 발생합니다. 즉 버전 변경에 따른 예기치 않은 결과가 나타나는 것이죠
이 경우 A 어셈블리뿐만 아니라 이를 참조하는 B 어셈블리도 같이 재 컴파일 해야만 문제를 해결할 수 있습니다.
이것은 앞서 설명한대로 const 상수는 이를 사용하는 어셈블리의 메타데이터로 그 값이 직접 기록되기 때문에 발생하는 문제입니다. 즉 A 어셈블리는 외부어셈블리이지만 B 어셈블리의 메타데이터로 상수 값 자체가 기록되어 버리기 때문에
A 어셈블리의 버전 업이 B어셈블리의 메타데이터를 변경하지는 못하는 것입니다
만일 A 어셈블리의 상수가 readonly 필드로 정의되어 있다면 이러한 문제로부터 자유로워 집니다
즉 readonly 필드의 값은 B 어셈블리의 메타데이터에 바로 기록되는 것이 아니라 외부 어셈블리를
참조하도록 기록되기 때문에 A어셈블리의 재 컴파일 만으로도 변경된 값이 적용되는 것입니다
이런 이유로 해서 ‘const 보다는 readonly를 사용하라’ 라고 하는 것입니다
만일 이런 상황이 아니라면 굳이 readonly 가 더 좋을 이유는 없습니다
자세한 내용이 궁금하시면 아래 글을 참고해 보기 바랍니다
http://mkexdev.net/Article/Content.aspx?parentCategoryID=1&categoryID=5&ID=515
그럼 이것으로 강좌를 마치도록 하겠습니다
좋은 하루 되세요~~