C# 기본 제공 형식

C# 기본 제공 형식 (Built-in types)

C#에는 언어 차원에서 미리 정해져 있는 "기본 제공 형식"들이 있어요. 이 형식들은 C# 키워드로 바로 사용할 수 있고, 대부분 .NET의 특정 타입을 가리키는 별칭(alias)입니다. 여기서는 값 형식과 참조 형식으로 나뉘는 기본 제공 형식들이 무엇인지, 그리고 특별한 취급을 받는 dynamic·delegate·void 같은 형식들이 어떤 점에서 다른지 하나씩 살펴볼게요.

출처: Microsoft Learn

본문

값 형식 기본 제공 형식

C#의 기본 제공 값 형식은 다음 표와 같아요. 왼쪽은 C#에서 쓰는 형식 키워드, 오른쪽은 이 키워드가 실제로 가리키는 .NET 타입입니다.

C# 형식 키워드 .NET 타입
bool xref:System.Boolean?displayProperty=nameWithType
byte xref:System.Byte?displayProperty=nameWithType
sbyte xref:System.SByte?displayProperty=nameWithType
char xref:System.Char?displayProperty=nameWithType
decimal xref:System.Decimal?displayProperty=nameWithType
double xref:System.Double?displayProperty=nameWithType
float xref:System.Single?displayProperty=nameWithType
int xref:System.Int32?displayProperty=nameWithType
uint xref:System.UInt32?displayProperty=nameWithType
nint xref:System.IntPtr?displayProperty=nameWithType
nuint xref:System.UIntPtr?displayProperty=nameWithType
long xref:System.Int64?displayProperty=nameWithType
ulong xref:System.UInt64?displayProperty=nameWithType
short xref:System.Int16?displayProperty=nameWithType
ushort xref:System.UInt16?displayProperty=nameWithType

참조 형식 기본 제공 형식

이번에는 C#의 기본 제공 참조 형식 표를 볼게요.

C# 형식 키워드 .NET 타입
object xref:System.Object?displayProperty=nameWithType
string xref:System.String?displayProperty=nameWithType
delegate xref:System.Delegate?displayProperty=nameWithType
dynamic xref:System.Object?displayProperty=nameWithType

[!NOTE] 이 문서의 예제는 언어 초기 버전 기준으로 설명합니다. 자세한 버전별 차이는 각 형식 문서를 참고하세요.

키워드는 .NET 타입의 별칭

앞의 두 표에서 왼쪽 열의 대부분의 C# 형식 키워드는, 오른쪽에 대응하는 .NET 타입의 별칭이에요. 그래서 둘은 서로 바꿔 쓸 수 있습니다(interchangeable). 예를 들어 다음 두 선언은 완전히 같은 타입의 변수를 선언하는 거예요.

int a = 123;
System.Int32 b = 123;

int라 적든 System.Int32라 적든 같은 32비트 정수형 변수가 만들어져요. 이름이 다를 뿐 실제 타입은 동일하다는 점, 기억해 둡시다.

dynamic 타입은 object와 뭐가 다른가

dynamic 타입은 겉보기에는 object와 비슷해 보여요. 하지만 중요한 차이가 있어요. dynamic으로 선언한 식은 컴파일 시점이 아니라 실행(runtime) 시점에 연산이 바인딩됩니다. 그리고 이런 차이도 따라옵니다.

  • dynamic 식에 대한 연산은 컴파일 시점이 아니라 실행 시점에 바인딩돼요.
  • new dynamic()은 사용할 수 없어요.
  • dynamic 타입에서 파생된 타입을 만들 수 없어요.

dynamic은 값이 아니라 "언제 어떻게 바인딩할지"를 정하는 동작 자체를 나타낸다고 생각하면 이해하기 쉬워요.

delegate 키워드

delegate 키워드도 기본 제공 참조 형식 키워드인데, 이 키워드로 선언하는 타입은 xref:System.Delegate?displayProperty=nameWithType에서 파생됩니다. 여기서 주의할 점이 하나 있어요. 다른 기본 제공 형식 키워드들과 달리 delegate는 특정 .NET 타입의 별칭이 아닙니다. 대신 추상 타입인 System.Delegate에서 파생된 사용자 정의 타입을 직접 선언하게 해 주는 키워드예요. 마찬가지로 dynamic도 특정 .NET 타입의 직접적인 별칭이라기보다는 실행 시점 바인딩 동작을 나타내는 개념이에요.

void 키워드

void 키워드는 **타입이 없음(absence of a type)**을 나타내요. 값을 반환하지 않는 메서드의 반환 타입으로 주로 사용합니다. 예를 들어 값을 돌려주지 않는 메서드를 선언할 때 void를 쓰는 식이죠.

Span<T>ReadOnlySpan<T>의 특별한 규칙

C# 언어에는 xref:System.Span`1?displayProperty=fullNamexref:System.ReadOnlySpan`1?displayProperty=fullName 타입에 대한 특별한 규칙이 마련되어 있어요. 이 두 타입은 기본 제공 형식으로 분류되지는 않습니다. 그 이유는 이 타입들에 대응하는 C# 키워드가 없기 때문이에요.

대신 C#은 배열 타입과 string 타입에서 Span<T>ReadOnlySpan<T>로 가는 **암시적 변환(implicit conversion)**을 정의해요. 덕분에 Span 타입들을 더 자연스러운 프로그래밍 시나리오에 녹여 쓸 수 있죠. 다음 변환들은 *암시적 span 변환(implicit span conversions)*으로 정의됩니다.

  • 요소 타입이 E1차원 배열에서 System.Span<E>
  • 요소 타입이 E인 1차원 배열에서, EU로 공변 변환(covariance conversion) 또는 동일 변환(identity conversion)을 가질 때 System.ReadOnlySpan<U>
  • EU로 공변 변환이나 동일 변환을 가질 때 System.Span<E>에서 System.ReadOnlySpan<U>
  • EU로 공변 변환이나 동일 변환을 가질 때 System.ReadOnlySpan<E>에서 System.ReadOnlySpan<U>
  • string에서 System.ReadOnlySpan<char>

여기서 "공변 변환"이란 타입이 더 파생된 타입에서 덜 파생된 타입으로 안전하게 변환되는 걸 의미한다고만 알아두면 돼요. 세부 내용이 궁금할 때는 차차 살펴보면 됩니다.

한 가지 더 중요한 규칙이 있어요. 적용 가능한 암시적 span 변환이 존재하는 곳이라면, 컴파일러는 사용자 정의 변환(user-defined conversion)을 절대 무시하지 않습니다. 그리고 암시적 span 변환은 확장 멤버의 수신자 매개 변수(receiver parameter)에도 적용될 수 있어요. 수신자 매개 변수는 확장 멤버에서 extension 키워드로 지정하며, this 한정자를 사용하는 확장 메서드의 첫 번째 매개 변수를 말합니다. 다만 메서드 그룹 변환(method group conversions)에는 암시적 span 변환이 고려되지 않아요.

더 알아보기