8가지 형식
8.1 일반
C# 언어의 형식은 참조 형식과 값 형식의 두 가지 주요 범주로 나뉩니다. 값 형식과 참조 형식은 둘 다 하나 이상의 형식 매개 변수를 포함하는 제네릭 형식일 수 있습니다. 형식 매개 변수는 값 형식과 참조 형식을 모두 지정할 수 있습니다.
type
: reference_type
| value_type
| type_parameter
| pointer_type // unsafe code support
;
pointer_type(§23.3)는 안전하지 않은 코드(§23)에서만 사용할 수 있습니다.
값 형식은 해당 데이터를 직접 포함하는 값 형식의 변수에서 참조 형식과 다르지만 참조 형식의 변수는 해당 데이터에 대한 참조를 저장하며, 후자는 개체라고 합니다. 참조 형식을 사용하면 두 변수가 동일한 개체를 참조할 수 있으므로 한 변수에 대한 작업이 다른 변수에서 참조하는 개체에 영향을 줄 수 있습니다. 값 형식의 경우 변수에는 각각 고유한 데이터 복사본이 있으며 한 변수에 대한 작업이 다른 변수에 영향을 줄 수 없습니다.
참고: 변수가 참조 또는 출력 매개 변수인 경우 자체 스토리지는 없지만 다른 변수의 스토리지를 참조합니다. 이 경우 ref 또는 out 변수는 사실상 고유 변수가 아닌 다른 변수의 별칭입니다. 끝 메모
C#의 형식 시스템은 모든 형식의 값을 개체로 처리할 수 있도록 통합됩니다. C#의 모든 형식은 object
클래스 형식에서 직접 또는 간접적으로 파생되고 object
는 모든 형식의 기본 클래스입니다. 참조 형식의 값은 object
로 인식함으로써 간단히 개체로 처리됩니다. 값 형식의 값은 boxing 및 unboxing 연산(§8.3.13)을 수행하여 개체로 처리됩니다.
편의를 위해 이 사양 전체에서 일부 라이브러리 형식 이름은 전체 이름 한정을 사용하지 않고 작성됩니다. 자세한 내용은 §C.5를 참조하세요.
8.2 참조 형식
8.2.1 일반
참조 형식은 클래스 형식, 인터페이스 형식, 배열 형식, 대리자 형식 또는 dynamic
형식입니다. nullable이 아닌 각 참조 형식에 대해 형식 이름에 추가하여 적어 둔 해당 nullable 참조 형식이 ?
있습니다.
reference_type
: non_nullable_reference_type
| nullable_reference_type
;
non_nullable_reference_type
: class_type
| interface_type
| array_type
| delegate_type
| 'dynamic'
;
class_type
: type_name
| 'object'
| 'string'
;
interface_type
: type_name
;
array_type
: non_array_type rank_specifier+
;
non_array_type
: value_type
| class_type
| interface_type
| delegate_type
| 'dynamic'
| type_parameter
| pointer_type // unsafe code support
;
rank_specifier
: '[' ','* ']'
;
delegate_type
: type_name
;
nullable_reference_type
: non_nullable_reference_type nullable_type_annotation
;
nullable_type_annotation
: '?'
;
pointer_type 안전하지 않은 코드(§23.3)에서만 사용할 수 있습니다. nullable_reference_type §8.9에서 더 자세히 설명합니다.
참조 형식 값은 형식의 인스턴스에 대한 참조이며, 후자는 개체라고 합니다. 특수 값 null
은 모든 참조 형식과 호환되며 인스턴스가 없음을 나타냅니다.
8.2.2 클래스 형식
클래스 형식은 데이터 멤버(상수 및 필드), 함수 멤버(메서드, 속성, 이벤트, 인덱서, 연산자, 인스턴스 생성자, 종료자 및 정적 생성자) 및 중첩된 형식을 포함하는 데이터 구조를 정의합니다. 클래스 형식은 파생 클래스가 기본 클래스를 확장하고 특수화할 수 있는 메커니즘인 상속을 지원합니다. 클래스 형식의 인스턴스는 object_creation_expression(§12.8.17.2)를 사용하여 생성됩니다.
클래스 형식은 §15에 설명되어 있습니다.
특정 미리 정의된 클래스 형식은 아래 표에 설명된 대로 C# 언어에서 특별한 의미를 갖습니다.
클래스 형식 | 설명 |
---|---|
System.Object |
다른 모든 형식의 궁극적인 기본 클래스입니다. §8.2.3을 참조하세요. |
System.String |
C# 언어의 문자열 형식입니다. §8.2.5를 참조하세요. |
System.ValueType |
모든 값 형식의 기본 클래스입니다. §8.3.2를 참조하세요. |
System.Enum |
모든 enum 형식의 기본 클래스입니다. §19.5를 참조하세요. |
System.Array |
모든 배열 형식의 기본 클래스입니다. §17.2.2를 참조하세요. |
System.Delegate |
모든 delegate 형식의 기본 클래스입니다. §20.1을 참조하세요. |
System.Exception |
모든 예외 형식의 기본 클래스입니다. §21.3을 참조하세요. |
8.2.3 개체 유형
object
클래스 형식은 다른 모든 형식의 최종 기본 클래스입니다. C#의 모든 형식은 클래스 형식에서 object
직접 또는 간접적으로 파생됩니다.
키워드 object
는 미리 정의된 클래스 System.Object
에 대한 별칭일 뿐입니다.
8.2.4 동적 형식
형식( dynamic
예: object
모든 개체)을 참조할 수 있습니다. 작업이 형식 dynamic
의 식에 적용되면 프로그램이 실행될 때까지 해당 확인이 지연됩니다. 따라서 참조된 개체에 작업을 합법적으로 적용할 수 없는 경우 컴파일 중에 오류가 발생하지 않습니다. 대신 런타임에 작업의 해결이 실패하면 예외가 throw됩니다.
이 dynamic
형식은 §8.7 및 §12.3.1의 동적 바인딩에 자세히 설명되어 있습니다.
8.2.5 문자열 형식
형식은 string
.에서 object
직접 상속되는 봉인된 클래스 형식입니다. 클래스의 string
인스턴스는 유니코드 문자열을 나타냅니다.
형식의 값은 string
문자열 리터럴(§6.4.5.6)로 작성할 수 있습니다.
키워드 string
는 미리 정의된 클래스 System.String
에 대한 별칭일 뿐입니다.
8.2.6 인터페이스 형식
인터페이스는 계약을 정의합니다. 인터페이스를 구현하는 클래스 또는 구조체는 해당 계약을 준수해야 합니다. 인터페이스는 여러 기본 인터페이스에서 상속할 수 있으며 클래스 또는 구조체는 여러 인터페이스를 구현할 수 있습니다.
인터페이스 형식은 §18에 설명되어 있습니다.
8.2.7 배열 형식
배열은 계산된 인덱스를 통해 액세스되는 0개 이상의 변수를 포함하는 데이터 구조입니다. 배열에 포함된 변수, 즉 배열의 요소라고도 하는 배열은 모두 같은 형식이며, 이 형식을 배열의 요소 형식이라고 합니다.
배열 형식은 §17에 설명되어 있습니다.
8.2.8 대리자 형식
대리자는 하나 이상의 메서드를 참조하는 데이터 구조입니다. 인스턴스 메서드의 경우 해당 개체 인스턴스도 참조합니다.
참고: C 또는 C++의 대리자와 가장 가까운 값은 함수 포인터이지만 함수 포인터는 정적 함수만 참조할 수 있지만 대리자는 정적 메서드와 인스턴스 메서드를 모두 참조할 수 있습니다. 후자의 경우 대리자는 메서드의 진입점에 대한 참조뿐만 아니라 메서드를 호출할 개체 인스턴스에 대한 참조도 저장합니다. 끝 메모
대리자 형식은 §20에 설명되어 있습니다.
8.3 값 형식
8.3.1 일반
값 형식은 구조체 형식 또는 열거형 형식입니다. C#은 단순 형식이라는 미리 정의된 구조체 형식 집합을 제공합니다. 단순 형식은 키워드를 통해 식별됩니다.
value_type
: non_nullable_value_type
| nullable_value_type
;
non_nullable_value_type
: struct_type
| enum_type
;
struct_type
: type_name
| simple_type
| tuple_type
;
simple_type
: numeric_type
| 'bool'
;
numeric_type
: integral_type
| floating_point_type
| 'decimal'
;
integral_type
: 'sbyte'
| 'byte'
| 'short'
| 'ushort'
| 'int'
| 'uint'
| 'long'
| 'ulong'
| 'char'
;
floating_point_type
: 'float'
| 'double'
;
tuple_type
: '(' tuple_type_element (',' tuple_type_element)+ ')'
;
tuple_type_element
: type identifier?
;
enum_type
: type_name
;
nullable_value_type
: non_nullable_value_type nullable_type_annotation
;
참조 형식의 변수와 달리 값 형식의 변수는 값 형식이 nullable 값 null
형식(§8.3.12)인 경우에만 값을 포함할 수 있습니다. null을 허용하지 않는 모든 값 형식에는 동일한 값 집합과 값을 null
나타내는 해당 null 허용 값 형식이 있습니다.
값 형식의 변수에 할당하면 할당되는 값의 복사본이 만들어집니다. 참조로 식별되는 개체는 복사하지 않고 참조 형식의 변수에 할당하는 것과 다릅니다.
8.3.2 System.ValueType 형식
모든 값 형식은 암시적으로 class
System.ValueType
상속되며, 이 형식은 클래스 object
에서 상속됩니다. 어떤 형식도 값 형식에서 파생될 수 없으며, 따라서 값 형식은 암시적으로 봉인됩니다(§15.2.2.3).
그 System.ValueType
자체가 value_type 아닙니다. 대신 모든 value_type 자동으로 파생되는 class_type.
8.3.3 기본 생성자
모든 값 형식은 기본 생성자라는 공용 매개 변수가 없는 인스턴스 생성자를 암시적으로 선언합니다. 기본 생성자는 값 형식의 기본값으로 알려진 초기화되지 않은 0개 인스턴스를 반환합니다.
- 모든 simple_type기본값은 모든 0의 비트 패턴으로 생성되는 값입니다.
- ,
sbyte
,byte
,short
,ushort
,int
uint
및long
의 경우ulong
기본값은 .입니다0
. - 의 경우
char
기본값은 .입니다'\x0000'
. - 의 경우
float
기본값은 .입니다0.0f
. - 의 경우
double
기본값은 .입니다0.0d
. - 의 경우
decimal
기본값은 크기가 0인 값 0입니다0m
. - 의 경우
bool
기본값은 .입니다false
. -
enum_type
E
경우 기본값은0
형식E
으로 변환됩니다.
- ,
-
struct_type 경우 기본값은 모든 값 형식 필드를 기본값으로 설정하고 모든 참조 형식 필드를
null
로 설정하여 생성되는 값입니다. -
nullable_value_type 기본값은 속성이 false인 인스턴스
HasValue
입니다. 기본값을 nullable 값 형식의 null 값이라고도 함 이러한 값의 속성을 읽으Value
려고 하면 형식System.InvalidOperationException
예외가 throw됩니다(§8.3.12).
다른 인스턴스 생성자와 마찬가지로 값 형식의 기본 생성자는 연산자를 사용하여 new
호출됩니다.
참고: 효율성상의 이유로 이 요구 사항은 구현에서 생성자 호출을 실제로 생성하도록 하기 위한 것이 아닙니다. 값 형식의 경우 기본값 식(§12.8.21)은 기본 생성자를 사용하는 것과 동일한 결과를 생성합니다. 끝 메모
예: 아래 코드에서 변수는
i
j
k
모두 0으로 초기화됩니다.class A { void F() { int i = 0; int j = new int(); int k = default(int); } }
끝 예제
모든 값 형식에는 암시적으로 공용 매개 변수가 없는 인스턴스 생성자가 있으므로 구조체 형식에 매개 변수가 없는 생성자의 명시적 선언을 포함할 수 없습니다. 그러나 구조체 형식은 매개 변수가 있는 인스턴스 생성자(§16.4.9)를 선언할 수 있습니다.
8.3.4 구조체 형식
구조체 형식은 상수, 필드, 메서드, 속성, 이벤트, 인덱서, 연산자, 인스턴스 생성자, 정적 생성자 및 중첩 형식을 선언할 수 있는 값 형식입니다. 구조체 형식의 선언은 §16에 설명되어 있습니다.
8.3.5 단순 형식
C#은 단순 형식이라는 미리 정의된 struct
형식 집합을 제공합니다. 간단한 형식은 키워드를 통해 식별되지만 이러한 키워드는 아래 표에 설명된 대로 네임스페이스의 미리 정의된 struct
형식에 System
대한 별칭일 뿐입니다.
키워드 | 별칭 형식 |
---|---|
sbyte |
System.SByte |
byte |
System.Byte |
short |
System.Int16 |
ushort |
System.UInt16 |
int |
System.Int32 |
uint |
System.UInt32 |
long |
System.Int64 |
ulong |
System.UInt64 |
char |
System.Char |
float |
System.Single |
double |
System.Double |
bool |
System.Boolean |
decimal |
System.Decimal |
단순 형식은 구조체 형식의 별칭이므로 모든 단순 형식에는 멤버가 있습니다.
예:
int
멤버가 선언되고System.Int32
멤버가 상속되며System.Object
다음 문이 허용됩니다.int i = int.MaxValue; // System.Int32.MaxValue constant string s = i.ToString(); // System.Int32.ToString() instance method string t = 123.ToString(); // System.Int32.ToString() instance method
끝 예제
참고: 단순 형식은 특정 추가 작업을 허용한다는 점에서 다른 구조체 형식과 다릅니다.
- 대부분의 단순 형식은 리터럴(§6.4.5)을 작성하여 값을 만들 수 있지만 C#은 일반적으로 구조체 형식의 리터럴을 프로비전하지 않습니다. 예:
123
형식의 리터럴이며int
형식'a'
char
의 리터럴입니다. 끝 예제- 식의 피연산자가 모두 단순 형식 상수인 경우 컴파일러가 컴파일 시간에 식을 평가할 수 있습니다. 이러한 식을 constant_expression(§12.23)라고 합니다. 다른 구조체 형식에서 정의한 연산자를 포함하는 식은 상수 식으로 간주되지 않습니다.
- 선언을 통해
const
단순 형식의 상수(§15.4)를 선언할 수 있습니다. 다른 구조체 형식의 상수는 가질 수 없지만 정적 읽기 전용 필드에서도 비슷한 효과가 제공됩니다.- 단순 형식과 관련된 변환은 다른 구조체 형식으로 정의된 변환 연산자 평가에 참여할 수 있지만 사용자 정의 변환 연산자는 다른 사용자 정의 변환 연산자(§10.5.3)의 평가에 참여할 수 없습니다.
끝 메모입니다.
8.3.6 정수 계열 형식
C#은 9개의 정수 계열 형식sbyte
(, , byte
, short
ushort
, int
uint
, long
ulong
및 char
)을 지원합니다. 정수 계열 형식에는 다음과 같은 크기 및 값 범위가 있습니다.
- 형식은
sbyte
값이 있는-128
부가적인 8비트 정수(포함)를127
나타냅니다. - 이 형식은
byte
값이 있는0
부호 없는 8비트 정수(포함)를255
나타냅니다. - 형식은
short
값이 있는 부가적인 16비트 정수(-32768
포함)를32767
나타냅니다. - 형식은
ushort
값을 포함하는 부호 없는 16비트 정0
수입니다65535
. - 형식은
int
부가적인 32비트 정수(포함)를-2147483648
2147483647
나타냅니다. - 형식은
uint
부호 없는 32비트 정수(포함)를0
4294967295
나타냅니다. - 형식은
long
값이 있는-9223372036854775808
부가적인 64비트 정수(포함)를9223372036854775807
나타냅니다. - 이 형식은
ulong
값이 있는0
부호 없는 64비트 정수(포함)를18446744073709551615
나타냅니다. - 형식은
char
값을 포함하는 부호 없는 16비트 정0
수입니다65535
. 형식에 사용할char
수 있는 값 집합은 유니코드 문자 집합에 해당합니다.참고: 표현은 동일
char
하지만ushort
한 형식에서 허용되는 모든 작업이 다른 형식에서 허용되는 것은 아닙니다. 끝 메모
서명된 모든 정수 계열 형식은 2의 보수 형식을 사용하여 표시됩니다.
integral_type 단항 및 이진 연산자는 §12.4.7에 자세히 설명된 대로 부호 있는 32비트 정밀도, 부호 없는 32비트 정밀도, 부호 있는 64비트 정밀도 또는 부호 없는 64비트 정밀도로 항상 작동합니다.
형식은 char
정수 형식으로 분류되지만 다른 정수 계열 형식과는 두 가지 방법으로 다릅니다.
- 다른 형식에서 형식으로 미리 정의된 암시적 변환은
char
없습니다. 특히 형식 및byte
형식에 형식을 사용하여ushort
완전히 나타낼 수 있는 값 범위가 있더라도char
sbyte, 바이트 또는ushort
char
존재하지 않는 암시적 변환이 있습니다. - 형식의
char
상수는 문자 형식에 대한 캐스트와 함께 character_literal또는 integer_literals로 작성되어야 합니다.
예:
(char)10
와 같습니다'\x000A'
. 끝 예제
checked
및 unchecked
연산자와 문은 정수 형식 산술 연산 및 변환에 대한 오버플로 검사를 제어하는 데 사용됩니다(§12.8.20).
checked
컨텍스트에서 오버플로는 컴파일 시간 오류를 생성하거나 System.OverflowException
throw됩니다.
unchecked
컨텍스트에서 오버플로는 무시되고 대상 형식에 맞지 않는 모든 상위 비트는 삭제됩니다.
8.3.7 부동 소수점 형식
C#은 두 개의 부동 소수점 형식 float
을 double
지원합니다. 및 float
형식은 double
다음 값 집합을 제공하는 32비트 단정밀도 및 64비트 배정밀도 IEC 60559 형식을 사용하여 표시됩니다.
- 양수 0 및 음수 0입니다. 대부분의 경우 양의 0과 음수 0은 단순 값 0과 동일하게 동작하지만 특정 연산은 두 연산을 구분합니다(§12.10.3).
- 양의 무한대 및 음의 무한대입니다. 무한대는 0이 아닌 숫자를 0으로 나누는 등의 연산에 의해 생성됩니다.
예:
1.0 / 0.0
양수 무한대를 생성하고 음의–1.0 / 0.0
무한대를 생성합니다. 끝 예제 - 숫자가 아닌 값으로, 종종 약어인 NaN입니다. NaN은 0으로 나누는 것과 같은 잘못된 부동 소수점 연산에 의해 생성됩니다.
- 양식의 0이 아닌 값 집합은 × m × 2e이고, 여기서 s는 1 또는 -1이고, m과 e는 특정 부동 소수점 유형에 따라 결정됩니다. 즉, 0m< 2gb 및 -149 ≤ < ≤ 104이고, ≤ 970에 대해 0m
double
2 및 -1075 <≤. < 비정규화된 부동 소수점 숫자는 0이 아닌 유효한 값으로 간주됩니다. C#은 규정 준수 구현이 비정규화된 부동 소수점 숫자를 지원하지 않아도 되거나 금지되지 않습니다.
이 형식은 float
전체 자릿수가 7자리인 약 1.5× 10⁻~ 3.4× 10에 이르는 값을 나타낼 수 있습니다.
이 형식은 double
전체 자릿수가 15~16자리인 약 5.0× 10⁻1.7× 10에 이르는 값을 나타낼 수 있습니다.
이진 연산자의 피연산자가 부동 소수점 형식인 경우 §12.4.7에 자세히 설명된 대로 표준 숫자 승격이 적용되고 연산이 정밀도 또는 float
정밀도로 double
수행됩니다.
할당 연산자를 포함한 부동 소수점 연산자는 예외를 생성하지 않습니다. 대신 예외적인 상황에서 부동 소수점 연산은 아래에 설명된 대로 0, 무한대 또는 NaN을 생성합니다.
- 부동 소수점 연산의 결과는 대상 형식에서 가장 가까운 표현 가능한 값으로 반올림됩니다.
- 부동 소수점 연산 결과의 크기가 대상 형식에 비해 너무 작으면 연산 결과는 양수 0 또는 음수 0이 됩니다.
- 부동 소수점 연산 결과의 크기가 대상 형식에 비해 너무 크면 작업의 결과는 무한대 또는 음의 무한대 상태가 됩니다.
- 부동 소수점 연산이 유효하지 않으면 작업의 결과가 NaN이 됩니다.
- 부동 소수점 연산의 피연산자 중 하나 또는 둘 다 NaN이면 작업의 결과가 NaN이 됩니다.
부동 소수점 연산은 작업의 결과 형식보다 높은 정밀도로 수행할 수 있습니다. 부동 소수점 형식의 값을 해당 형식의 정확한 정밀도로 강제 적용하려면 명시적 캐스트(§12.9.7)를 사용할 수 있습니다.
예: 일부 하드웨어 아키텍처는 형식보다
double
범위와 정밀도가 높은 "확장" 또는 "long double" 부동 소수점 형식을 지원하며, 이 높은 정밀도 형식을 사용하여 모든 부동 소수점 작업을 암시적으로 수행합니다. 성능의 과도한 비용에서만 이러한 하드웨어 아키텍처는 정밀도가 낮은 부동 소수점 작업을 수행하도록 만들 수 있으며, 성능과 정밀도를 모두 상실하기 위해 구현을 요구하는 대신 C#을 사용하면 모든 부동 소수점 작업에 더 높은 정밀도 형식을 사용할 수 있습니다. 보다 정확한 결과를 제공하는 것 외에는 측정 가능한 효과가 거의 없습니다. 그러나 곱셈이 범위를 벗어난x * y / z
결과를 생성하는 폼double
의 식에서 후속 나누기는 임시 결과를 다시 범위로 가져오며, 식이 더 높은 범위 형식으로double
평가된다는 사실은 무한대 대신 유한한 결과가 생성될 수 있습니다. 끝 예제
8.3.8 10진수 형식
decimal
형식은 재무 및 통화 계산에 적합한 128비트 데이터 형식입니다. 이 형식은 decimal
최소 -7.9 × 10⁻²에서 7.9 × 10gb까지의 값을 포함하여 28자리 이상의 정밀도를 나타낼 수 있습니다.
형식 decimal
의 한정된 값 집합은 형식(-1)v × c × 10⁻e, 여기서 부호 v는 0 또는 1이고 계수 c는 0≤ c<Cmax로 지정되고, 배율 e는 에민이 e ≤ Emax를 ≤, 여기서 Cmax는 1개 × 10개, 에민 ≤ 0은 0입니다. 및 Emax ≥ 28. 형식이 decimal
반드시 서명된 0, 무한대 또는 NaN을 지원하지는 않습니다.
A decimal
는 10의 힘만큼 크기가 조정된 정수로 표시됩니다. 절대값이 10진수보다 decimal
작은 s의 경우 1.0m
값은 28번째 소수점 이하의 위치와 정확히 일치합니다.
decimal
절대값이 28자리 이상1.0m
인 경우 값은 정확히 28자리 이상입니다.
float
데이터 형식 및 데이터 형식과 double
달리 소수 자릿수(예: 0.1
소수 자릿수)는 10진수 표현에서 정확하게 나타낼 수 있습니다.
float
및 double
표현에서 이러한 숫자에는 종료가 아닌 이진 확장이 있는 경우가 많으므로 이러한 표현은 반올림 오류가 발생하기 쉽습니다.
이진 연산자의 피연산자 중 decimal
하나가 형식이면 §12.4.7에 자세히 설명된 대로 표준 숫자 승격이 적용되고 정밀도로 double
연산이 수행됩니다.
형식 decimal
값에 대한 연산의 결과는 정확한 결과(각 연산자에 대해 정의된 배율 유지)를 계산한 다음 표현에 맞게 반올림한 결과입니다. 결과는 가장 가까운 표현 가능한 값으로 반올림되고, 결과가 두 개의 표현 가능한 값에 균등하게 가까운 경우 가장 낮은 유효 자릿수 위치에 짝수 숫자가 있는 값("은행의 반올림"이라고 함)으로 반올림됩니다. 즉, 결과는 적어도 28번째 소수점 이하의 위치와 정확히 같습니다. 반올림은 0이 아닌 값에서 0 값을 생성할 수 있습니다.
decimal
산술 연산이 형식에 비해 크기가 너무 큰 decimal
결과를 생성하면 throw System.OverflowException
됩니다.
형식의 정밀도는 decimal
더 크지만 부동 소수점 형식보다 범위가 작을 수 있습니다. 따라서 부동 소수점 형식에서 변환하여 오버플로 예외를 생성할 decimal
수 있으며, 부동 소수점 형식으로 decimal
변환하면 전체 자릿수 또는 오버플로 예외가 손실될 수 있습니다. 이러한 이유로 부동 소수점 형식과 명시적 캐스트가 없으면 부동 소수점과 decimal
decimal
피연산자를 같은 식에서 직접 혼합할 때 컴파일 시간 오류가 발생합니다.
8.3.9 부울 유형
이 형식은 bool
부울 논리 수량을 나타냅니다. 형식 bool
의 가능한 값은 다음과 같습니다true
false
. 표현 false
은 §8.3.3에 설명되어 있습니다. 표현 true
은 지정되지 않았지만 해당 표현과 다릅니다 false
.
표준 변환은 다른 값 형식 간에 bool
존재하지 않습니다. 특히 형식 bool
은 정수 계열 형식과 별개이며 정 bool
수 값 대신 값을 사용할 수 없으며 그 반대의 경우도 마찬가지입니다.
참고: C 및 C++ 언어에서는 0 정수 또는 부동 소수점 값 또는 null 포인터를 부울 값
false
으로 변환할 수 있으며 0이 아닌 정수 또는 부동 소수점 값 또는 null이 아닌 포인터를 부울 값true
으로 변환할 수 있습니다. C#에서 이러한 변환은 정수 또는 부동 소수점 값을 0과 명시적으로 비교하거나 개체 참조null
를 명시적으로 비교하여 수행됩니다. 끝 메모
8.3.10 열거형 형식
열거형 형식은 명명된 상수가 있는 고유 형식입니다. 모든 열거형 형식에는 기본 형식이 있습니다. 이 형식은 , byte
, sbyte
, short
, ushort
int
uint
또는 long
.입니다.ulong
열거형 형식의 값 집합은 기본 형식의 값 집합과 동일합니다. 열거형 형식의 값은 명명된 상수의 값으로 제한되지 않습니다. 열거형 형식은 열거형 선언(§19.2)을 통해 정의됩니다.
8.3.11 튜플 형식
튜플 형식은 선택적 이름과 개별 형식이 있는 정렬된 고정 길이 값 시퀀스를 나타냅니다. 튜플 형식의 요소 수를 해당 결과라고 합니다. 튜플 형식은 n ≥ 2로 작성 (T1 I1, ..., Tn In)
됩니다. 여기서 식별자는 I1...In
선택적 튜플 요소 이름입니다.
이 구문은 형식을 사용하여 생성된 형식 T1...Tn
System.ValueTuple<...>
의 약어이며, 2~7개 포함 사이의 모든 튜플 형식을 직접 표현할 수 있는 제네릭 구조체 형식 집합이어야 합니다.
해당 개수의 형식 매개 변수와 튜플 형식의 결과와 직접 일치하는 선언이 있을 System.ValueTuple<...>
필요가 없습니다. 대신, 7보다 큰 튜플은 튜플 요소 외에도 다른 System.ValueTuple<T1, ..., T7, TRest>
형식을 사용하여 나머지 요소 Rest
의 중첩된 값을 포함하는 필드를 갖는 제네릭 구조체 형식 System.ValueTuple<...>
으로 표시됩니다. 이러한 중첩은 필드의 존재와 같은 다양한 방법으로 관찰할 수 있습니다 Rest
. 단일 추가 필드만 필요한 경우 제네릭 구조체 형식 System.ValueTuple<T1>
이 사용됩니다. 이 형식은 그 자체로 튜플 형식으로 간주되지 않습니다. 7개가 넘는 추가 필드가 필요한 System.ValueTuple<T1, ..., T7, TRest>
경우 재귀적으로 사용됩니다.
튜플 형식 내의 요소 이름은 고유해야 합니다. 튜플 요소의 위치를 나타낼 수 있는 ItemX
시작되지 않은X
소수 자릿수 시퀀스인 양식0
의 튜플 요소 이름은 나타내는 위치에서만 허용됩니다X
.
선택적 요소 이름은 형식에 ValueTuple<...>
표시되지 않으며 튜플 값의 런타임 표현에 저장되지 않습니다. 요소 형식의 ID 변환 시퀀스가 있는 튜플 사이에 ID 변환(§10.2.2)이 있습니다.
new
튜플 형식 구문을 사용하여 §12.8.17.2new (T1, ..., Tn)
자를 적용할 수 없습니다. 튜플 값은 튜플 식(§12.8.6)에서 만들거나 생성된 형식new
에 직접 연산자를 적용하여 ValueTuple<...>
만들 수 있습니다.
튜플 요소는 이름 Item1
Item2
등이 있는 공용 필드이며 튜플 값(§12.8.7)의 멤버 액세스를 통해 액세스할 수 있습니다. 또한 튜플 형식에 지정된 요소의 이름이 있는 경우 해당 이름을 사용하여 해당 요소에 액세스할 수 있습니다.
참고: 큰 튜플이 중첩 값
System.ValueTuple<...>
으로 표현되더라도 각 튜플 요소는 해당 위치에 해당하는 이름으로 직접Item...
액세스할 수 있습니다. 끝 메모
예: 다음 예제가 제공됩니다.
(int, string) pair1 = (1, "One"); (int, string word) pair2 = (2, "Two"); (int number, string word) pair3 = (3, "Three"); (int Item1, string Item2) pair4 = (4, "Four"); // Error: "Item" names do not match their position (int Item2, string Item123) pair5 = (5, "Five"); (int, string) pair6 = new ValueTuple<int, string>(6, "Six"); ValueTuple<int, string> pair7 = (7, "Seven"); Console.WriteLine($"{pair2.Item1}, {pair2.Item2}, {pair2.word}");
에
pair1
대한pair2
튜플 형식이며 모두 유효하며pair3
이름은 아니요, 일부 또는 모든 튜플 형식 요소입니다.이름과 위치가 일치하기 때문에
pair4
튜플 형식Item1
은 유효하지만Item2
이름과 일치하지 않기 때문에pair5
Item2
튜플 형식Item123
은 허용되지 않습니다.선언은
pair6
pair7
튜플 형식이 양식ValueTuple<...>
의 생성된 형식과 교환 가능하고new
연산자가 후자의 구문으로 허용됨을 보여 줍니다.마지막 줄은 해당 위치에 해당하는 이름뿐만 아니라 형식에
Item
있는 경우 해당 튜플 요소 이름으로 튜플 요소에 액세스할 수 있음을 보여줍니다. 끝 예제
8.3.12 Nullable 값 형식
nullable 값 형식은 기본 형식 의 모든 값과 추가 null 값을 나타낼 수 있습니다. nullable 값 형식이 작성 T?
됩니다. 여기서 T
기본 형식입니다. 이 구문은 약식 System.Nullable<T>
이며 두 폼을 서로 바꿔 사용할 수 있습니다.
반대로 nullable이 아닌 값 형식은 해당 약식 System.Nullable<T>
이 아닌 T?
값 형식과 null을 허용하지 않는 값 형식(T
즉, 값 형식 제약 조건이 있는 모든 형식 매개 변수(§15.2.5)으로 제한되는 모든 형식 매개 변수입니다. 형식은 System.Nullable<T>
값 형식 제약 T
조건을 지정합니다. 즉, nullable 값 형식의 기본 형식은 nullable이 아닌 값 형식일 수 있습니다. nullable 값 형식의 기본 형식은 nullable 값 형식 또는 참조 형식일 수 없습니다. 예를 들어 int??
잘못된 형식입니다. Nullable 참조 형식은 §8.9에서 다룹니다.
nullable 값 형식 T?
의 인스턴스에는 두 개의 공용 읽기 전용 속성이 있습니다.
-
HasValue
형식의 속성bool
-
Value
형식의 속성T
null이 HasValue
아닌 인스턴스 true
라고 합니다. null이 아닌 인스턴스는 알려진 값을 포함하고 해당 값을 Value
반환합니다.
null이라고 하는 인스턴스 HasValue
입니다 false
. null 인스턴스에는 정의되지 않은 값이 있습니다. null 인스턴스를 Value
읽으려고 하면 throw System.InvalidOperationException
됩니다. nullable 인스턴스의 Value 속성에 액세스하는 프로세스를 래핑 해제라고 합니다.
기본 생성자 외에도 모든 nullable 값 형식 T?
에는 형식의 T
단일 매개 변수가 있는 공용 생성자가 있습니다. 형식 x
값 T
이 지정되면 폼의 생성자 호출입니다.
new T?(x)
는 속성이 null이 아닌 인스턴스 T?
를 Value
만듭니다 x
. 지정된 값에 대해 nullable 값 형식의 null이 아닌 인스턴스를 만드는 프로세스를 래핑이라고 합니다.
암시적 변환은 리터럴에서 null
(T?
) 및 (§10.2.6T
T?
사용할 수 있습니다.
nullable 값 형식 T?
은 인터페이스를 구현하지 않습니다(§18). 특히 이는 기본 형식 T
이 수행하는 인터페이스를 구현하지 않음을 의미합니다.
8.3.13 Boxing 및 unboxing
boxing 및 unboxing의 개념은 value_type 값을 형식 간에 변환할 수 있도록 허용하여 value_type s와 reference_typeobject
Boxing 및 unboxing을 사용하면 형식 시스템의 통합 보기를 사용할 수 있습니다. 여기서 모든 형식의 값은 궁극적으로 로 object
처리될 수 있습니다.
Boxing은 §10.2.9에서 더 자세히 설명하고 언박싱은 §10.3.7에 설명되어 있습니다.
8.4 생성된 형식
8.4.1 일반
제네릭 형식 선언은 그 자체로 형식 인수를 적용하여 다양한 형식을 형성하기 위해 "청사진"으로 사용되는 바인딩되지 않은 제네릭 형식을 표시합니다. 형식 인수는 제네릭 형식의 이름 바로 다음에 꺾쇠 괄호(<
및 >
)로 작성됩니다. 하나 이상의 형식 인수를 포함하는 형식을 생성된 형식이라고 합니다. 생성된 형식은 형식 이름이 나타날 수 있는 언어의 대부분의 위치에서 사용할 수 있습니다. 바인딩되지 않은 제네릭 형식은 typeof_expression 내에서만 사용할 수 있습니다(§12.8.18).
생성된 형식은 식에서 단순 이름(§12.8.4) 또는 멤버에 액세스할 때(§12.8.7)로 사용할 수도 있습니다.
namespace_or_type_name 평가되면 올바른 형식 매개 변수 수를 가진 제네릭 형식만 고려됩니다. 따라서 형식의 형식 매개 변수 수가 다르면 동일한 식별자를 사용하여 다른 형식을 식별할 수 있습니다. 이는 동일한 프로그램에서 제네릭 클래스와 제네릭이 아닌 클래스를 혼합할 때 유용합니다.
예제:
namespace Widgets { class Queue {...} class Queue<TElement> {...} } namespace MyApplication { using Widgets; class X { Queue q1; // Non-generic Widgets.Queue Queue<int> q2; // Generic Widgets.Queue } }
끝 예제
namespace_or_type_name 프로덕션의 이름 조회에 대한 자세한 규칙은 §7.8에 설명되어 있습니다. 이러한 프로덕션의 모호성 해결은 §6.2.5에 설명되어 있습니다.
type_name 형식 매개 변수를 직접 지정하지 않더라도 생성된 형식을 식별할 수 있습니다. 이 문제는 형식이 제네릭 class
선언 내에 중첩되고 포함하는 선언의 인스턴스 형식이 이름 조회(§15.3.9.7)에 암시적으로 사용되는 경우에 발생할 수 있습니다.
예제:
class Outer<T> { public class Inner {...} public Inner i; // Type of i is Outer<T>.Inner }
끝 예제
생성되지 않은 형식은 unmanaged_type(§8.8)로 사용할 수 없습니다.
8.4.2 형식 인수
형식 인수 목록의 각 인수는 단순히 형식입니다.
type_argument_list
: '<' type_arguments '>'
;
type_arguments
: type_argument (',' type_argument)*
;
type_argument
: type
| type_parameter nullable_type_annotation?
;
각 형식 인수는 해당 형식 매개 변수(§15.2.5)에 대한 제약 조건을 충족해야 합니다. null 허용 여부가 형식 매개 변수의 null 허용 여부와 일치하지 않는 참조 형식 인수는 제약 조건을 충족합니다. 그러나 경고가 발생할 수 있습니다.
8.4.3 열기 및 닫힌 형식
모든 형식은 열린 형식 또는 닫힌 형식으로 분류할 수 있습니다. 열린 형식은 형식 매개 변수를 포함하는 형식입니다. 즉,
- 형식 매개 변수는 열린 형식을 정의합니다.
- 배열 형식은 요소 형식이 열린 형식인 경우에만 열린 형식입니다.
- 생성된 형식은 하나 이상의 형식 인수가 열린 형식인 경우에만 열린 형식입니다. 생성된 중첩 형식은 하나 이상의 형식 인수 또는 포함하는 형식의 형식 인수가 열린 형식인 경우에만 열린 형식입니다.
닫힌 형식은 열린 형식이 아닌 형식입니다.
런타임에 제네릭 형식 선언 내의 모든 코드는 제네릭 선언에 형식 인수를 적용하여 생성된 닫힌 생성된 형식의 컨텍스트에서 실행됩니다. 제네릭 형식 내의 각 형식 매개 변수는 특정 런타임 형식에 바인딩됩니다. 모든 문 및 식의 런타임 처리는 항상 닫힌 형식에서 발생하며, 열린 형식은 컴파일 시간 처리 중에만 발생합니다.
두 개의 닫힌 생성된 형식은 동일한 바인딩되지 않은 제네릭 형식에서 생성된 경우 ID 변환 가능(§10.2.2)이며 해당 형식 인수 각각 간에 ID 변환이 존재합니다. 해당 형식 인수 자체는 ID 변환이 가능한 닫힌 생성된 형식 또는 튜플일 수 있습니다. ID 변환 가능한 닫힌 생성 형식은 단일 정적 변수 집합을 공유합니다. 그렇지 않으면 닫힌 생성된 각 형식에는 고유한 정적 변수 집합이 있습니다. 열려 있는 형식은 런타임에 존재하지 않으므로 열린 형식과 연결된 정적 변수가 없습니다.
8.4.4 바인딩된 형식 및 언바운드 형식
언바운드 형식이라는 용어는 제네릭이 아닌 형식 또는 언바운드 제네릭 형식을 나타냅니다. 바인딩된 용어 형식 은 제네릭이 아닌 형식 또는 생성된 형식을 나타냅니다.
언바운드 형식은 형식 선언으로 선언된 엔터티를 나타냅니다. 바인딩되지 않은 제네릭 형식은 그 자체가 형식이 아니며 변수, 인수 또는 반환 값의 형식 또는 기본 형식으로 사용할 수 없습니다. 언바운드 제네릭 형식을 참조할 수 있는 유일한 구문은 typeof
식입니다(§12.8.18).
8.4.5 충족 제약 조건
생성된 형식 또는 제네릭 메서드를 참조할 때마다 제공된 형식 인수는 제네릭 형식 또는 메서드(§15.2.5)에 선언된 형식 매개 변수 제약 조건에 대해 검사됩니다. 각 where
절에 대해 명명된 형식 매개 변수에 해당하는 형식 인수 A
는 다음과 같이 각 제약 조건에 대해 확인됩니다.
- 제약 조건이
class
형식, 인터페이스 형식 또는 형식 매개 변수인 경우 제약 조건에 표시되는 모든 형식 매개 변수로 대체되는 제공된 형식 인수를 사용하여 해당 제약 조건을 나타낼 수C
있습니다. 제약 조건을 충족하기 위해 형식A
을 다음 중 하나로 형식C
으로 변환할 수 있는 경우여야 합니다. - 제약 조건이 참조 형식 제약 조건(
class
)인 경우 형식A
은 다음 중 하나를 충족해야 합니다.-
A
는 인터페이스 형식, 클래스 형식, 대리자 형식, 배열 형식 또는 동적 형식입니다.
참고:
System.ValueType
System.Enum
이 제약 조건을 충족하는 참조 형식입니다. 끝 메모-
A
는 참조 형식(§8.2)으로 알려진 형식 매개 변수입니다.
-
- 제약 조건이 값 형식 제약 조건(
struct
)인 경우 형식A
은 다음 중 하나를 충족해야 합니다.-
A
는struct
형식 또는enum
형식이지만 nullable 값 형식은 아닙니다.
참고:
System.ValueType
System.Enum
이 제약 조건을 충족하지 않는 참조 형식입니다. 끝 메모-
A
는 값 형식 제약 조건(§15.2.5)을 갖는 형식 매개 변수입니다.
-
- 제약 조건이 생성자 제약 조건
new()
인 경우 형식A
은 매개 변수가 없는 공용 생성자가 아니abstract
어야 합니다. 다음 중 하나가 true이면 충족됩니다.
형식 매개 변수의 제약 조건 중 하나 이상이 지정된 형식 인수에 의해 충족되지 않는 경우 컴파일 시간 오류가 발생합니다.
형식 매개 변수는 상속되지 않으므로 제약 조건도 상속되지 않습니다.
예: 다음
D
에서는 기본T
T
매개 변수에 적용되는 제약 조건을 충족하도록class
형식 매개 변수B<T>
에 제약 조건을 지정해야 합니다. 반면에class
E
제약 조건을List<T>
IEnumerable
T
지정할 필요는 없습니다.class B<T> where T: IEnumerable {...} class D<T> : B<T> where T: IEnumerable {...} class E<T> : B<List<T>> {...}
끝 예제
8.5 형식 매개 변수
형식 매개 변수는 런타임에 매개 변수가 바인딩되는 값 형식 또는 참조 형식을 지정하는 식별자입니다.
type_parameter
: identifier
;
형식 매개 변수는 다양한 형식 인수로 인스턴스화할 수 있으므로 형식 매개 변수에는 다른 형식과 약간 다른 작업 및 제한이 있습니다.
참고: 다음과 같습니다.
- 형식 매개 변수는 기본 클래스(§15.2.4.2) 또는 인터페이스(§18.2.4)를 선언하는 데 직접 사용할 수 없습니다.
- 형식 매개 변수에 대한 멤버 조회 규칙은 형식 매개 변수에 적용되는 제약 조건(있는 경우)에 따라 달라집니다. 그들은 §12.5에 자세히 설명되어 있습니다.
- 형식 매개 변수에 사용할 수 있는 변환은 형식 매개 변수에 적용되는 제약 조건(있는 경우)에 따라 달라집니다. §10.2.12 및 §10.3.8에 자세히 설명되어 있습니다.
- 형식 매개 변수가 참조 형식(§10.2.12
null
으로 리터럴 을 변환할 수 없습니다. 그러나 기본 식(§12.8.21)을 대신 사용할 수 있습니다. 또한 형식 매개 변수에 지정된 형식의 값은 형식 매개 변수에 값 형식 제약 조건이 없는 한 null using 및==
(!=
)와 비교할 수 있습니다.new
식(§12.8.17.2)은 형식 매개 변수가 constructor_constraint 또는 값 형식 제약 조건(§15.2.5)으로 제한되는 경우에만 형식 매개 변수와 함께 사용할 수 있습니다.- 형식 매개 변수는 특성 내의 어느 곳에서도 사용할 수 없습니다.
- 형식 매개 변수는 멤버 액세스(§12.8.7) 또는 형식 이름(§7.8)에서 정적 멤버 또는 중첩된 형식을 식별하는 데 사용할 수 없습니다.
- 형식 매개 변수는 unmanaged_type(§8.8)로 사용할 수 없습니다.
끝 메모
형식으로 형식 매개 변수는 전적으로 컴파일 시간 구문입니다. 런타임에 각 형식 매개 변수는 제네릭 형식 선언에 형식 인수를 제공하여 지정된 런타임 형식에 바인딩됩니다. 따라서 형식 매개 변수로 선언된 변수의 형식은 런타임에 닫힌 생성된 형식 §8.4.3이 됩니다. 형식 매개 변수와 관련된 모든 문 및 식의 런타임 실행은 해당 매개 변수의 형식 인수로 제공된 형식을 사용합니다.
8.6 식 트리 형식
식 트리 를 사용하면 람다 식을 실행 코드 대신 데이터 구조로 나타낼 수 있습니다. 식 트리는 폼의 System.Linq.Expressions.Expression<TDelegate>
값이며 모든 TDelegate
대리자 형식입니다. 이 사양의 나머지 부분에서는 이러한 형식이 약식 Expression<TDelegate>
으로 참조됩니다.
람다 식에서 대리자 형식으로 변환이 있는 경우 식 트리 형식D
Expression<TDelegate>
으로의 변환도 존재합니다. 람다 식을 대리자 형식으로 변환하면 람다 식의 실행 코드를 참조하는 대리자가 생성되는 반면 식 트리 형식으로 변환하면 람다 식의 식 트리 표현이 만들어집니다. 이 변환에 대한 자세한 내용은 §10.7.3에서 제공됩니다.
예: 다음 프로그램은 람다 식을 실행 코드와 식 트리로 나타냅니다. 변환이 존재하기
Func<int,int>
때문에 변환도 다음과 같이Expression<Func<int,int>>
존재합니다.Func<int,int> del = x => x + 1; // Code Expression<Func<int,int>> exp = x => x + 1; // Data
이러한 할당에 따라 대리
del
자는 반환x + 1
되는 메서드를 참조하고 식 트리 exp는 식을x => x + 1
설명하는 데이터 구조를 참조합니다.끝 예제
Expression<TDelegate>
는 형식Compile
의 대리자를 생성하는 인스턴스 메서드 TDelegate
를 제공합니다.
Func<int,int> del2 = exp.Compile();
이 대리자를 호출하면 식 트리가 나타내는 코드가 실행됩니다. 따라서 위의 del
정의와 del2
동일하며 다음 두 문은 동일한 효과를 갖습니다.
int i1 = del(1);
int i2 = del2(1);
이 코드를 i1
i2
실행한 후 둘 다 값을 2
갖습니다.
제공된 API 표면은 위에서 설명한 Expression<TDelegate>
메서드에 대한 요구 사항을 초과하여 Compile
구현으로 정의됩니다.
참고: 식 트리에 제공된 API의 세부 정보는 구현에서 정의되지만 구현은 다음과 같습니다.
- 람다 식에서 변환한 결과로 생성된 식 트리의 구조를 검사하고 응답하는 코드를 사용하도록 설정합니다.
- 사용자 코드 내에서 프로그래밍 방식으로 식 트리를 만들 수 있도록 설정
끝 메모
8.7 동적 형식
이 형식은 다른 모든 형식 dynamic
에서 사용되는 정적 바인딩과 달리 §12.3.2에 자세히 설명된 대로 동적 바인딩을 사용합니다.
형식 dynamic
은 다음과 같은 경우를 제외하고 동일한 object
것으로 간주됩니다.
- 형식
dynamic
식에 대한 연산은 동적으로 바인딩할 수 있습니다(§12.3.3). - 형식 유추(§12.6.3)는 둘 다 후보인 경우보다
dynamic
선호object
합니다. -
dynamic
를 로 사용할 수 없습니다.- object_creation_expression 형식(§12.8.17.2)
- a class_base (§15.2.4)
- member_access predefined_type(§12.8.7.1)
- 연산자의 피연산자
typeof
- 특성 인수
- 제약 조건
- 확장 메서드 형식
- struct_interfaces(§16.2.5) 또는 interface_type_list(§15.2.4.1) 내에 있는 형식 인수의 모든 부분입니다.
이러한 동등성으로 인해 다음이 유지됩니다.
- 암시적 ID 변환이 있습니다.
- 사이
object
및dynamic
- 을(를) 바꿀
dynamic
때 동일한 생성된 형식 간object
- 바꿀
dynamic
때 동일한 튜플 형식 간object
- 사이
- 에 대한 암시적 및 명시적 변환
object
도 적용됩니다dynamic
. - 바꿀
dynamic
때 동일한 서명은 동일한 서명으로object
간주됩니다. - 형식
dynamic
은 런타임에 형식object
과 구별할 수 없습니다. - 형식
dynamic
의 식을 동적 식이라고 합니다.
8.8 관리되지 않는 형식
unmanaged_type
: value_type
| pointer_type // unsafe code support
;
unmanaged_type은 reference_type 또는 관리되지 않는 것으로 제한되지 않은 type_parameter가 아니며, 그 형식이 unmanaged_type이 아닌 인스턴스 필드를 포함하지 않는 모든 형식이다. 즉, unmanaged_type 다음 중 하나입니다.
-
sbyte
,byte
,short
,ushort
,int
,uint
,long
,ulong
,char
,float
,double
,decimal
또는bool
. - 모든 enum_type.
- 사용자 정의 struct_type은 unmanaged_type의 인스턴스 필드만 포함할 수 있습니다.
- 관리되지 않는 것으로 제한되는 모든 형식 매개 변수입니다.
- 모든 pointer_type (§23.3).
8.9 참조 형식 및 Null 허용 여부
8.9.1 일반
nullable 참조 형식은 nullable이 아닌 참조 형식에 nullable_type_annotation?
표시됩니다. nullable이 아닌 참조 형식과 해당 nullable 형식 간에 의미 체계 차이가 없습니다. 둘 다 개체에 대한 참조이거나 null
참조일 수 있습니다. nullable_type_annotation 존재하거나 없는 경우 식이 null 값을 허용하는지 여부를 선언합니다. 해당 의도에 따라 식이 사용되지 않는 경우 컴파일러가 진단을 제공할 수 있습니다. 식의 null 상태는 §8.9.5에 정의되어 있습니다. ID 변환은 nullable 참조 형식과 해당 nullable이 아닌 참조 형식(§10.2.2) 사이에 존재합니다.
참조 형식에는 두 가지 형태의 Null 허용 여부가 있습니다.
-
nullable: nullable-reference-type 을 할당
null
할 수 있습니다. 기본 null 상태는 maybe-null입니다. -
nullable이 아님: nullable이 아닌 참조
null
기본 null 상태는 null이 아닙니다.
참고: 형식
R
이며R?
동일한 기본 형식R
으로 표시됩니다. 해당 기본 형식의 변수는 개체에 대한 참조를 포함하거나 "참조 없음"을 나타내는 값null
이 될 수 있습니다. 끝 메모
nullable 참조 형식과 해당 nullable이 아닌 참조 형식 간의 구문 구분을 사용하면 컴파일러가 진단을 생성할 수 있습니다. 컴파일러는 §8.2.1에 정의된 대로 nullable_type_annotation 허용해야 합니다. 진단은 경고로 제한되어야 합니다. null 허용 주석의 존재 또는 부재 또는 nullable 컨텍스트의 상태는 컴파일 시간에 생성된 진단 메시지의 변경 내용을 제외하고 프로그램의 컴파일 시간 또는 런타임 동작을 변경할 수 없습니다.
8.9.2 nullable이 아닌 참조 형식
null을 허용하지 않는 참조 형식은 형식의 이름이 있는 T
폼T
의 참조 형식입니다. null을 허용하지 않는 변수의 기본 null 상태는 null이 아닙니다. null이 아닌 값이 필요한 경우 null 일 수 있는 식을 사용할 때 경고가 생성될 수 있습니다.
8.9.3 Nullable 참조 형식
폼 T?
의 참조 형식(예: string?
nullable 참조 형식)입니다. nullable 변수의 기본 null 상태는 null일 수 있습니다. 주석 ?
은 이 형식의 변수가 null을 허용한다는 의도를 나타냅니다. 컴파일러는 이러한 의도를 인식하여 경고를 실행할 수 있습니다. nullable 주석 컨텍스트를 사용하지 않도록 설정하면 이 주석을 사용하면 경고가 생성될 수 있습니다.
8.9.4 Nullable 컨텍스트
8.9.4.1 일반
소스 코드의 모든 줄에는 nullable 컨텍스트가 있습니다. nullable 컨텍스트 컨트롤 nullable 주석(§8.9.4.3) 및 null 허용 경고(§8.9.4.4)에 대한 주석 및 경고 플래그입니다. 각 플래그를 사용하거나 사용하지 않도록 설정할 수 있습니다. 컴파일러는 정적 흐름 분석을 사용하여 참조 변수의 null 상태를 확인할 수 있습니다. 참조 변수의 null 상태(§8.9.5)가 null이 아니거나 null이거나 기본값일 수 있습니다.
nullable 컨텍스트는 nullable 지시문(§6.5.9) 및/또는 소스 코드 외부의 일부 구현별 메커니즘을 통해 소스 코드 내에서 지정할 수 있습니다. 두 방법을 모두 사용하는 경우 nullable 지시문은 외부 메커니즘을 통해 만든 설정을 대체합니다.
nullable 컨텍스트의 기본 상태는 구현이 정의되어 있습니다.
이 사양 전체에서 null 허용 지시문이 포함되지 않거나 현재 null 허용 컨텍스트 상태에 대한 문이 없는 모든 C# 코드는 주석과 경고가 모두 사용되는 nullable 컨텍스트를 사용하여 컴파일된 것으로 가정해야 합니다.
참고: 두 플래그가 모두 비활성화된 nullable 컨텍스트는 참조 형식에 대한 이전 표준 동작과 일치합니다. 끝 메모
8.9.4.2 Nullable 사용 안 함
경고 플래그와 주석 플래그를 모두 사용하지 않도록 설정하면 null 허용 컨텍스트가 비활성화됩니다.
nullable 컨텍스트를 사용하지 않도록 설정한 경우:
- 주석이 지정되지 않은 참조 형식의 변수를 값으로 초기화하거나 값을
null
할당할 때 경고가 생성되지 않습니다. - null 값이 있을 수 있는 참조 형식의 변수인 경우 경고가 생성되지 않습니다.
- 모든 참조 형식
T
의 경우 주석은?
T?
메시지를 생성하고 형식T?
은 .와 동일합니다T
. - 모든 형식 매개 변수 제약 조건
where T : C?
의 경우 주석은?
C?
메시지를 생성하고 형식C?
은C
. - 모든 형식 매개 변수 제약 조건
where T : U?
의 경우 주석은?
U?
메시지를 생성하고 형식U?
은U
. - 제네릭 제약 조건은
class?
경고 메시지를 생성합니다. 형식 매개 변수는 참조 형식이어야 합니다.참고: 이 메시지는 관련이 없는 null 허용 경고 설정의 상태와 혼동하지 않도록 "경고"가 아닌 "정보"로 특징지어집니다. 끝 메모
- null-forgiving 연산자
!
(§12.8.9)는 영향을 주지 않습니다.
예제:
#nullable disable annotations string? s1 = null; // Informational message; ? is ignored string s2 = null; // OK; null initialization of a reference s2 = null; // OK; null assignment to a reference char c1 = s2[1]; // OK; no warning on dereference of a possible null; // throws NullReferenceException c1 = s2![1]; // OK; ! is ignored
끝 예제
8.9.4.3 Nullable 주석
경고 플래그를 사용하지 않도록 설정하고 주석 플래그를 사용하도록 설정하면 nullable 컨텍스트는 주석입니다.
nullable 컨텍스트가 주석인 경우:
- 모든 참조 형식
T
의 경우 주석?
T?
은 null 허용 형식을 나타내고 주석이T?
없는 형식은 null을 허용하지 않음을 나타냅니다T
. - Null 허용 여부와 관련된 진단 경고가 생성되지 않습니다.
- null 용서 연산자
!
(§12.8.9)는 피연산자의 분석된 null 상태와 생성되는 컴파일 시간 진단 경고를 변경할 수 있습니다.
예제:
#nullable disable warnings #nullable enable annotations string? s1 = null; // OK; ? makes s2 nullable string s2 = null; // OK; warnings are disabled s2 = null; // OK; warnings are disabled char c1 = s2[1]; // OK; warnings are disabled; throws NullReferenceException c1 = s2![1]; // No warnings
끝 예제
8.9.4.4 Nullable 경고
경고 플래그를 사용하도록 설정하고 주석 플래그를 사용하지 않도록 설정하면 nullable 컨텍스트는 경고입니다.
nullable 컨텍스트가 경고인 경우 컴파일러는 다음과 같은 경우에 진단을 생성할 수 있습니다.
- null일 수 있다고 판단된 참조 변수는 역참조됩니다.
- null을 허용하지 않는 형식의 참조 변수는 null일 수 있는 식에 할당됩니다.
- nullable
?
참조 형식을 적어 두는 데 사용됩니다. - null-forgiving 연산자
!
(§12.8.9)는 피연산자의 null 상태를 null이 아닌 상태로 설정하는 데 사용됩니다.
예제:
#nullable disable annotations #nullable enable warnings string? s1 = null; // OK; ? makes s2 nullable string s2 = null; // OK; null-state of s2 is "maybe null" s2 = null; // OK; null-state of s2 is "maybe null" char c1 = s2[1]; // Warning; dereference of a possible null; // throws NullReferenceException c1 = s2![1]; // The warning is suppressed
끝 예제
8.9.4.5 Nullable 사용
경고 플래그와 주석 플래그를 모두 사용하도록 설정하면 null 허용 컨텍스트가 활성화됩니다.
nullable 컨텍스트를 사용하는 경우:
- 모든 참조 형식
T
의 경우 주석?
은 null 허용 형식을 만드는T?
반면 주석이T?
없는 형식은 nullT
을 허용하지 않습니다. - 컴파일러는 정적 흐름 분석을 사용하여 참조 변수의 null 상태를 확인할 수 있습니다. nullable 경고를 사용하도록 설정하면 참조 변수의 null 상태(§8.9.5)가 null이 아니거나 null이거나 기본값일 수 있습니다.
- null-forgiving 연산자
!
(§12.8.9)는 피연산자의 null 상태를 null이 아닌 상태로 설정합니다. - 형식 매개 변수의 null 허용 여부가 해당 형식 인수의 null 허용 여부와 일치하지 않으면 컴파일러에서 경고를 실행할 수 있습니다.
8.9.5 Nullabilities 및 null 상태
컴파일러는 정적 분석을 수행할 필요가 없으며 null 허용 여부와 관련된 진단 경고를 생성할 필요가 없습니다.
이 하위 클래스의 나머지 부분에서는 조건부로 표준화됩니다.
진단 경고를 생성하는 컴파일러는 이러한 규칙을 준수합니다.
모든 식에는 다음 세 개의 null 상태중 하나가 있습니다.
- maybe null: 식의 값이 null로 계산할 수 있습니다.
- maybe default: 식의 값이 해당 형식의 기본값으로 계산할 수 있습니다.
- null이 아님: 식 값이 null이 아닙니다.
식의 기본 null 상태는 해당 형식 및 주석 플래그가 선언될 때의 상태에 따라 결정됩니다.
- nullable 참조 형식의 기본 null 상태는 다음과 같습니다.
- 주석 플래그가 사용되는 텍스트에 선언이 있는 경우 null일 수 있습니다.
- 주석 플래그가 비활성화된 텍스트에 선언이 있는 경우 null이 아닙니다.
- nullable이 아닌 참조 형식의 기본 null 상태는 null이 아닙니다.
참고: 형식이
default(T)
상태가 사용됩니다. null이 null을 허용하지 않는 형식의 도메인에 없으므로 상태는 기본값일 수 있습니다. 끝 메모
nullable이 아닌 참조 형식의 변수(§9.2.1)가 초기화되거나 주석 플래그가 사용되는 텍스트에서 해당 변수가 선언될 때 null일 수 있는 식에 할당될 때 진단을 생성할 수 있습니다.
예: 매개 변수가 null 허용되고 해당 값이 nullable이 아닌 형식에 할당되는 다음 메서드를 고려합니다.
#nullable enable public class C { public void M(string? p) { // Warning: Assignment of maybe null value to non-nullable variable string s = p; } }
컴파일러는 null일 수 있는 매개 변수가 null이 아니어야 하는 변수에 할당되는 경고를 발행할 수 있습니다. 매개 변수가 할당 전에 null로 확인된 경우 컴파일러는 null 허용 상태 분석에서 이 매개 변수를 사용하고 경고를 발생시키지 않을 수 있습니다.
#nullable enable public class C { public void M(string? p) { if (p != null) { string s = p; // No warning // Use s } } }
끝 예제
컴파일러는 분석의 일부로 변수의 null 상태를 업데이트할 수 있습니다.
예제: 컴파일러는 프로그램의 모든 문에 따라 상태를 업데이트하도록 선택할 수 있습니다.
#nullable enable public void M(string? p) { int length = p.Length; // Warning: p is maybe null string s = p; // No warning. p is not null if (s != null) { int l2 = s.Length; // No warning. s is not null } int l3 = s.Length; // Warning. s is maybe null }
이전 예제에서 컴파일러는 문장
int length = p.Length;
이후p
의 null 상태가 null이 아닌 상태로 변경되는지 결정할 수 있습니다. null이면 해당 문이 .를NullReferenceException
throw했을 것입니다. 이는 작성된 코드가 경고를 생성할 수 있다는 점을 제외하고 코드가 선행된if (p == null) throw NullReferenceException();
경우의 동작과 유사하며, 그 목적은 예외가 암시적으로 throw될 수 있음을 경고하는 것입니다. 끝 예제
메서드의 뒷부분에서 코드는 null 참조가 아닌지 s
확인합니다. null-checked 블록이 닫힌 후 null 상태가 s
null일 수 있습니다. 컴파일러는 코드가 null일 수 있다고 가정하도록 작성되었기 때문에 s
null일 수 있다고 유추할 수 있습니다. 일반적으로 코드에 null 검사가 포함된 경우 컴파일러는 값이 null일 수 있다고 유추할 수 있습니다.
예제: 다음 식 각각에는 null 검사의 일부 형태가 포함됩니다.
o
의 null 상태는 다음 각 문 이후에 null이 아닐 때도 null이 될 수도 있는 상태로 변경될 수 있습니다.#nullable enable public void M(string s) { int length = s.Length; // No warning. s is not null _ = s == null; // Null check by testing equality. The null state of s is maybe null length = s.Length; // Warning, and changes the null state of s to not null _ = s?.Length; // The ?. is a null check and changes the null state of s to maybe null if (s.Length > 4) // Warning. Changes null state of s to not null { _ = s?[4]; // ?[] is a null check and changes the null state of s to maybe null _ = s.Length; // Warning. s is maybe null } }
자동 속성 및 필드와 유사한 이벤트 선언은 모두 컴파일러에서 생성된 지원 필드를 사용합니다. Null 상태 분석은 이벤트 또는 속성에 대한 할당이 컴파일러에서 생성된 백업 필드에 대한 할당임을 유추할 수 있습니다.
예제: 컴파일러는 자동 속성이나 필드 유사 이벤트를 작성할 때, 해당 컴파일러가 생성한 지원 필드를 기록한다는 것을 결정할 수 있습니다. 속성의 null 상태는 지원 필드의 null 상태와 일치합니다.
class Test { public string P { get; set; } public Test() {} // Warning. "P" not set to a non-null value. static void Main() { var t = new Test(); int len = t.P.Length; // No warning. Null state is not null. } }
이전 예제에서 생성자는
P
을 null이 아닌 값으로 설정하지 않으며, 컴파일러가 경고를 발생시킬 수 있습니다. 속성의 형식이 nullable이 아닌 참조 형식이므로P
속성에 액세스할 때는 경고가 없습니다. 끝 예제
컴파일러는 속성(§15.7)을 상태의 변수로 처리하거나 독립적인 get 및 set 접근자(§15.7.3)로 처리할 수 있습니다.
예제: 컴파일러는 속성에 쓰는 것이 속성 읽기의 null 상태를 변경할지 또는 속성을 읽는 경우 해당 속성의 null 상태를 변경할지 여부를 선택할 수 있습니다.
class Test { private string? _field; public string? DisappearingProperty { get { string tmp = _field; _field = null; return tmp; } set { _field = value; } } static void Main() { var t = new Test(); if (t.DisappearingProperty != null) { int len = t.DisappearingProperty.Length; // No warning. A compiler can assume property is stateful } } }
이전 예제에서 백업 필드는
DisappearingProperty
읽을 때 null로 설정됩니다. 그러나 컴파일러는 속성을 읽어도 해당 식의 null 상태가 변경되지 않는다고 가정할 수 있습니다. 끝 예제
컴파일러는 변수, 속성 또는 이벤트를 역참조하는 식을 사용하여 null 상태를 null이 아닌 상태로 설정할 수 있습니다. null인 경우 역참조 식이 NullReferenceException
을 던졌을 것입니다.
예제:
public class C { private C? child; public void M() { _ = child.child.child; // Warning. Dereference possible null value var greatGrandChild = child.child.child; // No warning. } }
끝 예제
조건부 표준 텍스트의 끝
ECMA C# draft specification