* const
C++와 다르게 C# const(constant의 줄임말이다.) 키워드는 변수의 생명 주기동안 변경되지 않는 상수 변수를 위해 사용된다. 선언과 동시에 상수 변수에 값을 할당해야 한다. 선언 시 값이 상수 변수에 할당되고 난 이후에는 변경되지 않는다.
상수 변수의 값은 컴파일 타임 값, const 키워드를 통해 선언되는 변수는 컴파일 타임 상수이다.
// C++ constexpr과 비슷한 것 같다.
상수 객체를 선언하기 위해 const 키워드를 사용할 수 없다. const 키워드는 기본 데이터 타입(int, float, string 등)에서만 사용 가능하다.
const string constString = "test text";
// 기본 데이터 타입에 대해서만 const사용할 수 있다.
// 클래스 객체는 불가능하다.
// const MyClass constMyClass1 = new MyClass();
// null을 할당하는 것은 가능하다.
const MyClass? constMyClass2 = null;
오른쪽에는 상수가 와야하는데 new MyClass()는 상수가 아니기 때문에 불가능하다.
* readonly
- 정의
readonly 키워드는 읽기전용 변수를 정의하기 위해 사용된다. 변수는 클래스 스코프나 생성자에서만 값을 할당 받을 수 있다. 다른 곳에서는 readonly 변수의 값을 변경할 수 없다.
readonly는 크게 다음과 같은 위치에서 사용될 수 있다.
- readonly (필드) : 해당 필드가 선언이나 생성자에서만 할당될 수 있음을 나타낸다.
- readonly struct : struct가 불변임을 나타낸다. readonly struct의 모든 필드는 readonly여야 한다.
- readonly (struct의 필드) : 해당 struct 필드가 struct의 상태를 변경하지 않음을 나타낸다.
- ref readonly (메소드) : 참조 값을 반환하지만 수정할 수 없다.
- 동작 방식
readonly 필드는 생성자 이후에 할당될 수 없다. 값 타입, 참조 타입 마찬가지로 필드의 값은 변경할 수 없다.
class ReadonlyTestClass1
{
public ReadonlyTestClass1(int input)
{
Value = input;
}
public readonly int Value;
}
class ReadonlyTestClass2
{
public ReadonlyTestClass2(string input)
{
Value = input;
}
public readonly string Value;
}
static void Main(string[] args)
{
/* 값 타입 */
ReadonlyTestClass1 readonlyTestClass1 = new ReadonlyTestClass1(3);
// 변경할 수 없다.
//readonlyTestClass1.Value = 3;
//==================================================================================================
/* 불변 참조 타입 */
ReadonlyTestClass2 readonlyTestClass2 = new ReadonlyTestClass2("text");
string tempString = new string("text1");
// 새로운 문자열을 가리키도록 변경할 수 없다.
//readonlyTestClass2.Value = tempString;
}
ref readonly를 반환하는 경우 다음과 같이 작성하면 된다.
class ReturnRefReadonly
{
public ref int getValue() { return ref value; }
public ref readonly int getValueReadonly() { return ref value; }
private int value = 3;
}
static void Main(string[] args)
{
ReturnRefReadonly rrr = new ReturnRefReadonly();
ref int temp1 = ref rrr.getValue();
ref readonly int temp2 = ref rrr.getValueReadonly();
//temp2 = 5; // readonly라서 불가능하다.
}
- 주의 사항
@ 데이터를 참조하는 값(힙의 메모리 주소 값이라고 생각해도 될 것 같다.)과 실제 데이터의 값
위에서 readonly 필드의 값이 변경되지 않음을 알 수 있었다. 하지만 참조 타입의 경우 readonly 필드의 값은 특정 메모리를 가리키는 주소 값 같은 것이기 때문에 문제가 생길 수 있다. 실제 데이터 관점에서 하나씩 따져보면 다음과 같다.
- 값 타입 : readonly 변수가 데이터를 직접 포함하고 있기 때문에 데이터를 변경할 수 없다. 데이터가 보호된다.
- 불변 참조 타입 : readonly 변수이기 때문에 항상 동일한 불변 데이터를 참조해야 한다. 불변 데이터이기 때문에 데이터가 보호된다.
- 가변 참조 타입 : readonly 변수이기 때문에 항상 동일한 데이터를 참조해야 한다. 하지만 데이터는 변경될 수 있다.
1, 2번은 데이터가 보호된다. 위에서 확인했다.
하지만 3번 가변 참조 타입일 때는 가리키는 데이터만 변경되지 않을 뿐 실제 내용은 변경될 수 있다.
class ReadonlyTestClass3
{
public ReadonlyTestClass3(StringBuilder input)
{
Value = input;
}
public readonly StringBuilder Value;
}
static void Main(string[] args)
{
/* 참조 타입 */
ReadonlyTestClass3 readonlyTestClass3 = new ReadonlyTestClass3(new StringBuilder("text"));
StringBuilder tempStringBuilder = new StringBuilder("text1");
// 새로운 문자열을 기리키도록 변경할 수 없다.
//readonlyTestClass3.Value = tempStringBuilder;
// 하지만 readonly가 적용되는 것은 가리키고 있는 참조 정보이다. 참조 정보만 건드리지 않으면 원본을 수정해도 문제없다.
tempStringBuilder.Append("text1"); // 컴파일 가능
}
위의 코드에서 필드 Value는 StringBuilder가 할당된 힙 공간을 가리키고 있는 참조 값이다. 해당 힙 공간을 가리키는 사실만 변경하지 않는다면 실제로 데이터가 들어있는 StrinbBuilder의 내용을 바꿔도 상관없다. 이러한 보안 취약점 때문에 MS에서는 가능한 readonly 가변 참조 타입으로 선언하지 말라고 되어있다.
https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/readonly
readonly keyword - C# Reference
Table of contents readonly (C# Reference) Article 06/18/2022 3 minutes to read 14 contributors In this article --> The readonly keyword is a modifier that can be used in four contexts: In a field declaration, readonly indicates that assignment to the field
docs.microsoft.com
@ C++에서의 해결 방법
참고로 C++에서는 이러한 문제점을 막기 위해 가리키는 대상의 불변과 대상의 데이터 불변을 const 키워드를 사용하여 따로 설정할 수 있도록 되어있다.
string stringValue1("text1");
string stringValue2("text2");
//===============================================================
/* const DataType* 형식 */
const string* constStringPtr = &stringValue1;
// 가리키고 있는 대상의 데이터를 수정할 수 없다.
//constStringPtr[0] = 'a';
// 가리키는 대상을 수정할 수 있다.
constStringPtr = &stringValue2;
//===============================================================
/* DataType* const 형식 */
string* const stringPtrConst = &stringValue1;
// 가리키고 있는 대상의 데이터를 수정할 수 있다.
stringPtrConst[0] = 'a';
// 가리키는 대상을 수정할 수 없다.
//stringPtrConst = &stringValue2;
//===============================================================
/* const DataType* const 형식 */
const string* const constStringConstPtr = &stringValue1;
// 가리키고 있는 대상의 데이터를 수정할 수 없다.
//constStringConstPtr[0] = 'a';
// 가리키는 대상을 수정할 수 없다.
//constStringConstPtr = &stringValue2;
@ C#에서의 대안
C#에서는 C++에서 원본 데이터의 수정을 막아주는 const와 같은 도구가 없다. 이 문제를 해결하기 위해서는 다음과 같은 방법을 생각해볼 수 있다.
- 참조 객체를 반환하는데 원본 객체를 변경 시키지 않으려면 원본을 반환하지 말고 깊은 복사를 통해 새로운 객체를 생성하여 반환한다. 문제점은 단순히 객체를 읽었을 뿐인데 힙 메모리에 새로운 객체가 할당되고 가비지 컬렉터는 이 쓰레기를 처리하기 위해 고생한다는 점이다.
- 참조 객체를 직접 반환하는 메소드 대신 객체에서 원하는 값만 복사하여 반환하는 메소드를 제공한다.
* const vs readonly
const는 컴파일 타임 상수를 정의할 때 사용한다. 필드의 선언에서만 초기화할 수 있다. 생성자에서 수정할 수 없다.
readonly는 필드의 선언과 생성자에서 여러 번 할당 가능하다. 생성자에 따라서 다른 값을 가질 수 있다. 런타임 상수로 사용할 수 있다.
class Const_ReadOnly
{
public Const_ReadOnly(int value)
{
// 변경할 수 없다.
//ConstValue = value;
// 생성자에서 얼마든지 변경가능하다.
ReadonlyValue = value;
if(0 == value)
{
ReadonlyValue = -1;
}
}
public Const_ReadOnly(int value1, int value2)
{
ReadonlyValue = value1 % value2;
}
public const int ConstValue = 5;
public readonly int ReadonlyValue = 5;
}
'C#' 카테고리의 다른 글
[C#] abstract (0) | 2022.06.28 |
---|---|
[C#] abstract class vs interface (0) | 2022.06.28 |
[C#] ref, out (0) | 2022.06.28 |
[C#] 강력한 형식 언어(Strongly Typed Language) (0) | 2022.06.28 |
[C#] var (0) | 2022.06.28 |