참고 항목
이 문서에서는 nullable 참조 형식을 설명합니다. nullable 값 형식을 선언할 수도 있습니다.
nullable 인식 컨텍스트에 있는 코드에서 nullable 참조 형식을 사용합니다. nullable 참조 형식, null 정적 분석 경고 및 null 허용 연산자는 선택적 언어 기능입니다. 모두 기본적으로 꺼져 있습니다. 빌드 설정을 사용하거나 pragmas를 사용하여 코드에서 프로젝트 수준에서 nullable 컨텍스트 를 제어합니다.
C# 언어 참조는 가장 최근에 릴리스된 C# 언어 버전을 문서화합니다. 또한 예정된 언어 릴리스의 공개 미리 보기 기능에 대한 초기 설명서도 포함되어 있습니다.
설명서는 언어의 마지막 세 버전 또는 현재 공개 미리 보기에서 처음 도입된 기능을 식별합니다.
팁 (조언)
C#에서 기능이 처음 도입된 시기를 찾으려면 C# 언어 버전 기록에 대한 문서를 참조하세요.
중요합니다
모든 프로젝트 템플릿은 프로젝트에서 nullable 컨텍스트을 활성화합니다. 이전 템플릿으로 만든 프로젝트에는 이 요소가 포함되지 않으며, 프로젝트 파일에서 사용하도록 설정하거나 pragma를 사용하지 않는 한 이러한 기능은 해제됩니다.
nullable 인식 컨텍스트에서:
- null이 아닌 값을 사용하여 참조 형식
T의 변수를 초기화해야 하며, 해당 값을null할당할 수 없습니다. - 참조 형식
T?null의 변수를 초기화하거나 할당null할 수 있지만 역참조 전에 확인해야null합니다. - null-forgiving 연산자를 형식
m의 변수T?에m!적용하는 경우와 같이 변수는 null이 아닌 것으로 간주됩니다.
컴파일러는 이전 규칙을 사용하여 nullable이 아닌 참조 형식 T 과 nullable 참조 형식 T? 을 구분합니다.
T 형식의 변수와 T? 형식의 변수는 동일한 .NET 형식입니다. 다음 예제에서는 null을 허용하지 않는 문자열 및 nullable 문자열을 선언하고 null 허용 연산자를 사용하여 null을 허용하지 않는 문자열에 값을 할당합니다.
string notNull = "Hello";
string? nullable = default;
notNull = nullable!; // null forgiveness
변수와 notNullnullable 둘 다 형식을 String 사용합니다. nullable이 아닌 형식과 nullable 형식은 모두 동일한 형식을 사용하므로 여러 위치에서 nullable 참조 형식을 사용할 수 없습니다. 일반적으로 nullable 참조 형식을 기본 클래스 또는 구현된 인터페이스로 사용할 수 없습니다. 개체 만들기 또는 형식 테스트 식에서는 nullable 참조 형식을 사용할 수 없습니다. 멤버 액세스 식의 형식으로 nullable 참조 형식을 사용할 수 없습니다. 다음 예제에서는 다음 구문을 보여 줍니다.
public MyClass : System.Object? // not allowed
{
}
var nullEmpty = System.String?.Empty; // Not allowed
var maybeObject = new object?(); // Not allowed
try
{
if (thing is string? nullableString) // not allowed
Console.WriteLine(nullableString);
} catch (Exception? e) // Not Allowed
{
Console.WriteLine("error");
}
nullable 참조 및 정적 분석
이전 섹션의 예제에서는 nullable 참조 형식의 특성을 보여 줍니다. nullable 참조 형식은 새 클래스 형식이 아니라 기존 참조 형식의 주석입니다. 컴파일러는 해당 주석을 사용하여 코드에서 잠재적 null 참조 오류를 찾을 수 있습니다. null을 허용하지 않는 참조 형식과 nullable 참조 형식 간에는 런타임 차이가 없습니다. 컴파일러는 null을 허용하지 않는 참조 형식에 대한 런타임 검사를 추가하지 않습니다. 컴파일 시간 분석에는 이점이 있습니다. 컴파일러는 코드에서 잠재적 null 오류를 찾고 해결하는 데 도움이 되는 경고를 생성합니다. 의도를 선언하고 코드가 해당 의도를 위반하면 컴파일러가 경고를 표시합니다.
중요합니다
Nullable 참조 주석은 동작 변경을 도입하지 않지만 다른 라이브러리는 리플렉션을 사용하여 nullable 및 nullable이 아닌 참조 형식에 대해 다른 런타임 동작을 생성할 수 있습니다. 특히 Entity Framework Core는 nullable 특성을 읽습니다. nullable 참조를 선택적 값으로 해석하고 null을 허용하지 않는 참조를 필수 값으로 해석합니다.
nullable 사용 컨텍스트에서 컴파일러는 nullable 참조 형식 및 null을 허용하지 않는 참조 형식의 변수에서 정적 분석을 수행합니다. 컴파일러는 각 참조 변수의 null-state를 not-null 또는 maybe-null로 추적합니다. null을 허용하지 않는 참조의 기본 상태는 not-null입니다. null 허용 참조의 기본 상태는 maybe-null입니다.
null을 허용하지 않는 참조 형식은 해당 null-state가 not-null이므로 항상 안전하게 역참조해야 합니다. 해당 규칙을 적용하기 위해 null을 허용하지 않는 참조 형식이 null이 아닌 값으로 초기화되지 않는 경우 컴파일러는 경고를 실행합니다. 선언할 지역 변수를 할당해야 합니다. 모든 필드에는 필드 이니셜라이저 또는 모든 생성자에 not-null 값이 할당되어야 합니다. 상태가 maybe-null인 참조에 null을 허용하지 않는 참조가 할당되는 경우 컴파일러가 경고를 실행합니다. 일반적으로 null을 허용하지 않는 참조는 null이 아니며 해당 변수를 역참조할 때 경고가 발생하지 않습니다.
참고 항목
null을 허용하지 않는 참조 형식에 maybe-null 식을 할당하면 컴파일러가 경고를 생성합니다. 그런 다음, 컴파일러는 not-null 식에 할당될 때까지 해당 변수에 대한 경고를 생성합니다.
nullable 참조 형식을 초기화하거나 할당 null 할 수 있습니다. 따라서 정적 분석에서는 역참조되기 전에 변수가 not-null인지 확인해야 합니다. nullable 참조가 null이 아닌 참조 변수에 할당하면 컴파일러 경고가 생성됩니다. 다음 클래스는 해당 경고의 예를 보여 줍니다.
public class ProductDescription
{
private string shortDescription;
private string? detailedDescription;
public ProductDescription() // Warning! shortDescription not initialized.
{
}
public ProductDescription(string productDescription) =>
this.shortDescription = productDescription;
public void SetDescriptions(string productDescription, string? details=null)
{
shortDescription = productDescription;
detailedDescription = details;
}
public string GetDescription()
{
if (detailedDescription.Length == 0) // Warning! dereference possible null
{
return shortDescription;
}
else
{
return $"{shortDescription}\n{detailedDescription}";
}
}
public string FullDescription()
{
if (detailedDescription == null)
{
return shortDescription;
}
else if (detailedDescription.Length > 0) // OK, detailedDescription can't be null.
{
return $"{shortDescription}\n{detailedDescription}";
}
return shortDescription;
}
}
다음 코드 조각은 이 클래스를 사용하는 경우 컴파일러가 경고를 내보내는 위치를 보여 줍니다.
string shortDescription = default; // Warning! non-nullable set to null;
var product = new ProductDescription(shortDescription); // Warning! static analysis knows shortDescription maybe null.
string description = "widget";
var item = new ProductDescription(description);
item.SetDescriptions(description, "These widgets will do everything.");
앞의 예제에서는 참조 변수의 null-state를 확인하는 컴파일러의 정적 분석을 보여 줍니다. 컴파일러는 null 검사 및 할당에 대한 언어 규칙을 적용하여 분석에 대해 알립니다. 컴파일러는 메서드 또는 속성의 의미 체계를 가정할 수 없습니다. Null 검사를 수행하는 메서드를 호출하는 경우 컴파일러는 해당 메서드가 변수의 null-state에 영향을 준다는 것을 알 수 없습니다. API에 특성을 추가하여 컴파일러에 인수 및 반환 값의 의미 체계를 알릴 수 있습니다. .NET 라이브러리의 많은 일반적인 API에는 이러한 특성이 있습니다. 예를 들어, 컴파일러는 IsNullOrEmpty을 null 검사로서 올바르게 해석합니다. null-state 정적 분석에 적용되는 특성에 관한 자세한 내용은 null 허용 특성 문서를 참조하세요.
Nullable 컨텍스트
nullable 컨텍스트는 컴파일러가 nullable 참조 형식 주석을 처리하는 방법과 정적 null 상태 분석 중에 생성되는 경고를 결정합니다. nullable 컨텍스트에는 주석 설정과 경고 설정의 두 가지 플래그가 포함됩니다.
주석 및 경고 설정은 모두 기존 프로젝트에 대해 기본적으로 사용하지 않도록 설정됩니다. .NET 6(C# 10)부터 두 플래그는 기본적으로 새로운 프로젝트에 대해 사용하도록 설정됩니다. nullable 컨텍스트에 대한 두 가지 고유 플래그의 이유는 nullable 참조 형식이 도입되기 전에 대규모 프로젝트를 보다 쉽게 마이그레이션할 수 있도록 하기 위해서입니다.
소규모 프로젝트의 경우 null 허용 참조 형식을 사용하도록 설정하고 경고를 수정한 후 계속할 수 있습니다. 그러나 대규모 프로젝트 및 다중 프로젝트 솔루션의 경우 해당 프로세스에서 많은 수의 경고를 생성할 수 있습니다. null 허용 참조 형식을 사용하기 시작할 때 pragma를 사용하여 파일별로 null 허용 참조 형식을 사용하도록 설정할 수 있습니다. System.NullReferenceException을 throw하는 것을 방지하는 새 기능은 기존 코드베이스에서 사용할 때 중단을 유발할 수 있습니다.
- 명시적 형식의 참조 변수는 모두 null을 허용하지 않는 참조 형식으로 해석됩니다.
- 제네릭의
class제약 조건의 의미가 null을 허용하지 않는 참조 형식을 의미하도록 변경되었습니다. - 이러한 새 규칙으로 인해 새로운 경고가 생성됩니다.
null 허용 주석 컨텍스트가 컴파일러의 동작을 결정합니다. nullable 컨텍스트 설정에 대해 네 가지 조합이 있습니다.
- 비활성화된
: 코드는 nullable-oblivious . Disable은 새 구문이 오류 대신 경고를 생성한다는 점을 제외하면 null 허용 참조 형식이 사용하도록 설정되기 전의 동작과 일치합니다. - null 허용 경고가 사용되지 않습니다.
- 모든 참조 형식 변수는 nullable 참조 형식입니다.
-
?접미사를 사용하여 null 허용 참조 형식을 선언하면 경고가 생성됩니다. - null forgiving 연산자인
!를 사용할 수 있지만 영향은 없습니다.
-
둘 다 사용 설정됨: 컴파일러는 모든 null 참조 분석과 모든 언어 기능을 활성화합니다.
- 모든 새 null 허용 경고가 사용됩니다.
-
?접미사를 사용하여 null 허용 참조 형식을 선언할 수 있습니다. -
?접미사가 없는 참조 형식 변수는 null을 허용하지 않는 참조 형식입니다. - null 용서 연산자는 가능한 역참
null조에 대한 경고를 표시하지 않습니다.
-
경고 사용: 컴파일러는 모든 null 분석을 수행하고 코드가
null를 역참조할 수 있을 때 경고를 발생시킨다.- 모든 새 null 허용 경고가 사용됩니다.
-
?접미사를 사용하여 null 허용 참조 형식을 선언하면 경고가 생성됩니다. - 모든 참조 형식 변수는 null일 수 있습니다. 그러나 멤버는 접미사로 선언되지 않은 경우 모든 메서드의 여는 괄호에서 null-state가
?입니다. - null forgiving 연산자인
!를 사용할 수 있습니다.
- 사용하도록 설정된 주석
: 코드가 역참조하거나 null을 허용하지 않는 변수에 null 식을 할당할 때 컴파일러는 경고를 내보내지 않습니다. - 모든 새로운 null 가능 경고가 비활성화됩니다.
-
?접미사를 사용하여 null 허용 참조 형식을 선언할 수 있습니다. -
?접미사가 없는 참조 형식 변수는 null을 허용하지 않는 참조 형식입니다. - null forgiving 연산자인
!를 사용할 수 있지만 영향은 없습니다.
.csproj 파일의 요소를 사용하여 <Nullable> 프로젝트에 대해 nullable 주석 컨텍스트 및 nullable 경고 컨텍스트를 설정할 수 있습니다. 이 요소는 컴파일러가 형식의 null 허용 여부와 내보내는 경고를 해석하는 방법을 구성합니다. 다음 표에서는 허용 가능한 값을 보여주고 지정한 컨텍스트를 요약합니다.
| 컨텍스트 | 역참조 경고 | 할당 경고 | 참조 유형 |
? 접미사 |
! 연산자 |
|---|---|---|---|---|---|
disable |
비활성화 | 비활성화 | 모두 null 값을 가질 수 있습니다. | 경고 생성 | 아무런 영향이 없습니다. |
enable |
활성화됨 | 활성화됨 |
?로 선언하지 않는 한 null을 허용하지 않음 |
nullable 형식 선언 | 가능한 null 할당에 대한 경고를 표시하지 않습니다. |
warnings |
활성화됨 | 적용할 수 없음 | 모두 null 허용이지만 메서드의 여는 중괄호에서 멤버는 not-null로 간주됩니다. | 경고 생성 | 가능한 null 할당에 대한 경고를 표시하지 않습니다. |
annotations |
비활성화 | 비활성화 |
?로 선언하지 않는 한 null을 허용하지 않음 |
nullable 형식 선언 | 아무런 영향이 없습니다. |
disabled 컨텍스트에서 컴파일된 코드의 참조 형식 변수는 null 인식 불가입니다.
null 리터럴이나 maybe-null 변수를 nullable 무지인 변수에 할당할 수 있습니다. 그러나 nullable-oblivious 변수의 기본 상태는 not-null입니다.
프로젝트에 가장 적합한 설정을 선택합니다.
- 진단이나 새로운 기능을 기반으로 업데이트하지 않으려는 레거시 프로젝트에 대해서는 사용 중지를 선택합니다.
- 경고를 선택하여 코드에서 System.NullReferenceException이 발생할 수 있는 위치를 확인합니다. null을 허용하지 않는 참조 형식을 사용하도록 코드를 수정하기 전에 이러한 경고를 해결할 수 있습니다.
- 경고를 사용하기 전에 설계 의도를 표현하려면 annotations를 선택합니다.
- null 참조 예외로부터 보호하려는 새 프로젝트와 활성 프로젝트에 대해 사용을 선택합니다.
예제:
<Nullable>enable</Nullable>
지시문을 사용하여 소스 코드의 어디에서나 이러한 동일한 플래그를 설정할 수도 있습니다. 이러한 지시문은 대규모 코드베이스를 마이그레이션할 때 가장 유용합니다.
: 주석 및 경고 플래그를사용하도록 설정합니다. -
#nullable disable: 주석 및 경고 플래그를 설정하여사용하지 않도록 합니다. -
#nullable restore: 주석 플래그 및 경고 플래그를 프로젝트 설정으로 복원합니다. -
#nullable disable warnings: 사용하지 않도록 경고 플래그를 설정합니다. -
#nullable enable warnings: 사용하도록 설정할 경고 플래그를 설정합니다. -
#nullable restore warnings: 경고 플래그를 프로젝트 설정으로 복원합니다. -
#nullable disable annotations: 사용하지 않도록 주석 플래그를 설정합니다. -
#nullable enable annotations: 사용하도록 설정할 주석 플래그를 설정합니다. -
#nullable restore annotations: 주석 플래그를 프로젝트 설정으로 복원합니다.
모든 코드 줄에 대해 다음 조합 중 원하는 것을 설정할 수 있습니다.
| 경고 플래그 | 주석 플래그 | 사용 |
|---|---|---|
| 프로젝트 기본값 | 프로젝트 기본값 | 기본값 |
| enable | disable | 분석 경고 수정 |
| enable | 프로젝트 기본값 | 분석 경고 수정 |
| 프로젝트 기본값 | enable | 형식 주석 추가 |
| enable | enable | 이미 마이그레이션된 코드 |
| disable | enable | 경고를 수정하기 전에 코드에 주석 달기 |
| disable | disable | 마이그레이션된 프로젝트에 레거시 코드 추가 |
| 프로젝트 기본값 | disable | 거의 없음 |
| disable | 프로젝트 기본값 | 거의 없음 |
이러한 9가지 조합을 사용하면 컴파일러가 코드에 대해 내보내는 진단을 세밀하게 제어할 수 있습니다. 아직 해결할 준비가 되지 않은 추가 경고를 표시하지 않고도 업데이트 중인 모든 영역에서 더 많은 기능을 사용하도록 설정할 수 있습니다.
중요합니다
전역 nullable 컨텍스트는 생성된 코드 파일에 적용되지 않습니다. 두 전략 중 어느 것을 사용하든, 생성된 것으로 표시된 모든 소스 파일에 대해 null 허용 컨텍스트는 비활성화됨입니다. 이 조건은 컴파일러가 생성된 파일의 API에 주석을 달지 않음을 의미합니다. 컴파일러는 생성된 파일에 대해 nullable 경고를 생성하지 않습니다. 파일은 다음 네 가지 방법 중 하나로 생성된 것으로 표시됩니다.
- .editorconfig에서 해당 파일에 적용되는 섹션에
generated_code = true를 지정합니다. - 파일의 맨 위에 있는 주석에
<auto-generated>또는<auto-generated/>를 배치합니다. 해당 주석의 모든 줄에 넣을 수 있지만 주석 블록은 파일의 첫 번째 요소여야 합니다. - 파일 이름을 TemporaryGeneratedFile_로 시작합니다.
- 파일 이름을 .designer.cs, .generated.cs, .g.cs 또는 .g.i.cs로 종료합니다.
생성기는 전처리기 지시문을 사용하여 #nullable 옵트인할 수 있습니다.
기본적으로 nullable 주석 및 경고 플래그는 비활성화되어있습니다. 이 기본값은 기존 코드가 변경 없이 새 경고를 생성하지 않고 컴파일된다는 것을 의미합니다. .NET 6부터 새 프로젝트에는 모든 프로젝트 템플릿에 <Nullable>enable</Nullable> 요소가 포함되어 이러한 플래그를 enabled로 설정합니다.
이러한 옵션은 null 허용 참조 형식을 사용하도록 기존 코드베이스를 업데이트하는 두 가지 고유한 전략을 제공합니다.
nullable 컨텍스트 설정
두 가지 방법으로 nullable 컨텍스트를 제어할 수 있습니다. 프로젝트 수준에서 프로젝트 설정을 추가합니다 <Nullable>enable</Nullable> . 단일 C# 소스 파일에서 pragma를 #nullable enable 추가하여 nullable 컨텍스트를 사용하도록 설정합니다. 자세한 내용은 nullable 전략 설정을 참조하세요. .NET 6 이전에는 새 프로젝트에서 기본 <Nullable>disable</Nullable>사용합니다. .NET 6부터 새 프로젝트에는 프로젝트 파일에 <Nullable>enable</Nullable> 요소가 포함됩니다.
제네릭
형식 매개 변수를 nullable 매개 T?변수T로 사용하는 경우 실제 형식 인수는 해석 방법을 ? 결정합니다. 다음 제네릭 선언을 고려합니다.
public class Box<T>
{
public T Contents { get; set; }
}
형식 매개 변수는 참조 형식 또는 값 형식 T? 을 사용할 수 있으므로 의미는 호출자가 제공하는 형식 인수에 따라 달라집니다. 다음 규칙은 제약 조건이 없는 경우에 T 해결되는 사항을 T? 설명합니다.
- 형식 인수는 nullable이 아닌 참조 형식입니다. 의 경우, 즉
T?string해당 nullable 참조 형식입니다string?.Box<string>T - 형식 인수가 값 형식입니다. 의 경우는
int동일한 값 형식입니다.intT?Box<int>T형식 매개 변수struct에 제약 조건이 없으면 주석이 값 형식에 영향을 주지 않습니다. 이 경우T?(int?)를 의미합니다 Nullable<T> . - 형식 인수는 이미 null을 허용합니다. 의 경우
Box<string?>, isstring?및T?is.string?T"이중 nullable" 형식은 가져올 수 없습니다.
제약 조건은 허용되는 형식 인수를 제한합니다. 또한 컴파일러에서 사용할 수 있는 방법에 T 대한 이유를 제공합니다.
-
where T : class에는 nullable이 아닌 참조 형식이 필요합니다.Box<string>가 허용됩니다.Box<string?>는 경고를 생성합니다. -
where T : class?는 nullable 또는 nullable이 아닌 참조 형식을 허용합니다. 둘 다Box<string>허용Box<string?>됩니다. -
where T : struct에는 nullable이 아닌 값 형식이 필요합니다.Box<int>가 허용됩니다.Box<int?>하지 않습니다. 이 제약 조건을T?사용하면 제네릭 평균 Nullable<T>(forBox<int>,T?is) 내에 있습니다int?. -
where T : notnull에는 nullable이 아닌 참조 또는 값 형식이 필요합니다.Box<string>허용Box<int>됩니다Box<string?>. 경고를 생성합니다. -
where T : BaseType에서 파생BaseType되는 nullable이 아닌 참조 형식이 필요합니다.?nullable 파생 형식도 허용하도록 추가(where T : BaseType?)합니다.
제약 조건은 제네릭 형식 매개 변수를 사용하는 방법에 대한 컴파일러 이유에서 도움이 됩니다.
public static T? FirstOrDefault<T>(IEnumerable<T> source)
{
foreach (T item in source)
{
return item;
}
return default;
}
public static void RequireNotNull<T>(T value) where T : notnull
{
ArgumentNullException.ThrowIfNull(value);
}
public static void Generics()
{
string? first = FirstOrDefault<string>([]);
Console.WriteLine(first ?? "<empty>");
RequireNotNull("not null");
}
C# 언어 사양
자세한 내용은 C# 언어 사양의 Nullable 참조 형식 섹션을 참조하세요.
참고 항목
.NET