- 변수를 역참조하기 전에 변수가
null일 수 있는지 여부를 확인하는 정적 흐름 분석이 향상되었습니다. - 흐름 분석에서 null-state를 확인하도록 API에 주석을 다는 특성입니다.
- 개발자가 변수에 대해 의도하는 null-state를 명시적으로 선언하는 데 사용하는 변수 주석입니다.
컴파일러는 컴파일 시간에 코드에 있는 모든 식의 null-state를 추적합니다. null 상태 다음 두 값 중 하나가 있습니다.
-
not-null: 식이 not-
null로 알려져 있습니다. -
maybe-null: 식은
null일 수 있습니다.
변수 주석은 참조 형식 변수의 Null 허용 여부를 결정합니다.
-
null을 허용하지 않음: 변수에
null값이나 maybe-null 식을 할당하면 컴파일러가 경고를 표시합니다. null을 허용하지 않는 변수의 기본 null-state는 not-null입니다. -
null 허용: 변수에
null값 또는 maybe-null 식을 할당할 수 있습니다. 변수의 null-state가 maybe-null인 경우 변수를 역참조하면 컴파일러는 경고를 표시합니다. 변수의 기본 null-state는 maybe-null입니다.
이 문서의 나머지 부분에서는 코드에서 null 값을 역참조할 가능성이 있을 때, 세 가지 기능 영역이 경고를 생성하는 방법을 설명합니다. 변수를 역참조하는 것은 다음 예와 같이 .(점) 연산자를 사용하여 해당 멤버 중 하나에 액세스하는 것을 의미합니다.
string message = "Hello, World!";
int length = message.Length; // dereferencing "message"
값이 null인 변수를 역참조하면 런타임에서 System.NullReferenceException을 throw합니다.
마찬가지로, 개체가 null인 경우 [] 표기법을 사용하여 개체의 멤버에 액세스할 때 경고가 발생할 수 있습니다.
using System;
public class Collection<T>
{
private T[] array = new T[100];
public T this[int index]
{
get => array[index];
set => array[index] = value;
}
}
public static void Main()
{
Collection<int> c = default;
c[10] = 1; // CS8602: Possible dereference of null
}
학습 내용은 다음과 같습니다.
- 컴파일러의 null-state 분석: 컴파일러가 식이 not-null 또는 maybe-null인지 확인하는 방법입니다.
- 컴파일러의 null-state 분석을 위해 더 많은 컨텍스트를 제공하는 API에 적용되는 특성입니다.
- 변수에 대한 개발자의 의도를 나타내는 null 허용 변수 주석입니다. 주석은 필드, 매개변수 및 반환 값의 기본 null 상태를 설정하는 데 유용합니다.
-
제네릭 형식 인수를 관리하는 규칙입니다. 형식 매개 변수가 참조 형식 또는 값 형식일 수 있으므로 새로운 제약 조건이 추가되었습니다.
?접미사는 null 허용 값 형식과 null 허용 참조 형식에 대해 다르게 구현됩니다. - Nullable 컨텍스트는 대규모 프로젝트를 마이그레이션하는 데 도움을 줍니다.. 마이그레이션할 때 앱의 일부에서 nullable 컨텍스트에서 경고 및 주석을 사용하도록 설정할 수 있습니다. 더 많은 경고를 해결한 후 전체 프로젝트에 대해 두 설정을 모두 사용하도록 설정할 수 있습니다.
마지막으로 struct 형식 및 배열의 null-state 분석에 대해 알려진 문제에 대해 알아봅니다.
C#의 null 허용 안전성에 대한 학습 모듈에서 이러한 개념을 살펴볼 수도 있습니다.
Null 상태 분석
Null 상태 분석 참조의 null 상태 추적합니다. 식은 not-null 또는 maybe-null입니다. 컴파일러는 다음 두 가지 방법으로 변수가 not-null임을 확인합니다.
- 변수에 null이 아닌
것으로 알려진 값이 할당되었습니다. - 변수가
null과 비교 확인되었으며, 그 이후로 할당되지 않았습니다.
컴파일러가 확인할 수 없는 변수는 null이 아님 으로 식별되지 않으면 null일 가능성이 있다고간주됩니다. 분석에서는 실수로 null 값을 역참조할 수 있는 상황에 대해 경고를 제공합니다. 컴파일러는 null-state를 기반으로 경고를 생성합니다.
- 변수가 null이 아닌
경우 해당 변수를 안전하게 역참조할 수 있습니다. - 변수가 maybe-null인 경우 해당 변수를 역참조하기 전에 검사하여 변수가
null이 아닌지 확인해야 합니다.
다음 예제를 참조하세요.
string? message = null;
// warning: dereference null.
Console.WriteLine($"The length of the message is {message.Length}");
var originalMessage = message;
message = "Hello, World!";
// No warning. Analysis determined "message" is not-null.
Console.WriteLine($"The length of the message is {message.Length}");
// warning!
Console.WriteLine(originalMessage.Length);
앞의 예제에서 컴파일러는 첫 번째 메시지가 인쇄될 때 message가 maybe-null임을 확인합니다. 두 번째 메시지에 대한 경고는 없습니다. 마지막 코드 줄은 originalMessage가 null일 수 있으므로 경고를 생성합니다. 다음 예는 노드 트리를 루트로 트래버스하고 트래버스 중에 각 노드를 처리하는 더 실용적인 용도를 보여 줍니다.
void FindRoot(Node node, Action<Node> processNode)
{
for (var current = node; current != null; current = current.Parent)
{
processNode(current);
}
}
이전 코드는 current 변수를 역참조하기 위한 경고를 생성하지 않습니다. 정적 분석에서 current가 maybe-null이면 이 변수를 역참조하지 않는 것으로 확인합니다.
current에 액세스하기 전과 current를 ProcessNode 작업에 전달하기 전에 null 변수가 current.Parent인지 검사됩니다. 이전 예제에서는 지역 변수가 초기화되거나 할당되었을 때, 또는
null-state 분석은 호출된 메서드를 추적하지 않습니다. 따라서 모든 생성자가 호출하는 공통 도우미 메서드에서 초기화된 필드는 다음 메시지와 함께 경고를 생성할 수 있습니다.
null을 허용하지 않는 속성 'name'은 생성자를 종료할 때 non-null 값을 포함해야 합니다.
생성자 연결 또는 도우미 메서드의 null 허용 특성이라는 두 가지 방법 중 하나로 이러한 경고를 해결할 수 있습니다. 다음 코드에서는 각 예제를 보여 줍니다.
Person 클래스는 다른 모든 생성자가 호출하는 공통 생성자를 사용합니다.
Student 클래스에는 System.Diagnostics.CodeAnalysis.MemberNotNullAttribute 특성으로 주석이 달린 도우미 메서드가 있습니다.
using System.Diagnostics.CodeAnalysis;
public class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
public Person(string firstName, string lastName)
{
FirstName = firstName;
LastName = lastName;
}
public Person() : this("John", "Doe") { }
}
public class Student : Person
{
public string Major { get; set; }
public Student(string firstName, string lastName, string major)
: base(firstName, lastName)
{
SetMajor(major);
}
public Student(string firstName, string lastName) :
base(firstName, lastName)
{
SetMajor();
}
public Student()
{
SetMajor();
}
[MemberNotNull(nameof(Major))]
private void SetMajor(string? major = default)
{
Major = major ?? "Undeclared";
}
}
nullable 상태 분석 및 컴파일러가 생성하는 경고는 null을 역참조함에 따라 프로그램 오류를 방지하는 데 도움이 됩니다.
null 허용 경고 해결에 대한 문서에서는 코드에 가장 많이 나타날 가능성이 있는 경고를 수정하는 기술을 제공합니다. null 상태 분석에서 생성된 진단은 경고일 뿐입니다.
API 시그니처의 특성
null-state 분석에는 API의 의미 체계를 이해하기 위한 개발자의 힌트가 필요합니다. 일부 API는 null 검사를 제공하며 변수의 null-state를 maybe-null에서 not-null로 변경합니다. 다른 API는 입력 인수의 null-state에 따라 not-null 또는 maybe-null인 식을 반환합니다. 예를 들어, 메시지를 대문자로 표시하는 다음 코드를 살펴봅니다.
void PrintMessageUpper(string? message)
{
if (!IsNull(message))
{
Console.WriteLine($"{DateTime.Now}: {message.ToUpper()}");
}
}
bool IsNull(string? s) => s == null;
검사를 기반으로 개발자는 이 코드를 안전한 것으로 간주하며 코드가 경고를 생성해서는 안 됩니다. 그러나 컴파일러는 IsNull이 null 검사를 제공하고 message.ToUpper()를 message 변수로 간주하여 문에 대해 경고를 발급한다는 사실을 알지 못합니다. 이 경고를 수정하려면 NotNullWhen 특성을 사용합니다.
bool IsNull([NotNullWhen(false)] string? s) => s == null;
이 특성은 IsNull이 false를 반환하는 경우 매개 변수 s가 null이 아님을 컴파일러에 알립니다. 컴파일러는 if (!IsNull(message)) {...} 블록 내에서 message의 null 상태를 not-null로 변경합니다. 경고가 발급되지 않습니다.
특성은 멤버를 호출하는 데 사용되는 개체 인스턴스의 인수, 반환 값, 멤버의null-state에 대해 자세한 정보를 제공합니다. 각 특성에 대한 자세한 내용은 null 허용 참조 특성에 대한 언어 참조 문서에서 찾을 수 있습니다. .NET 5부터는 모든 .NET 런타임 API에 주석이 추가됩니다. 인수 및 반환 값의 null-state에 대한 의미 체계 정보를 제공하도록 고유 API에 주석을 달아 정적 분석을 개선합니다.
null 허용 변수 주석
null-state 분석은 지역 변수에 대한 강력한 분석을 제공합니다. 멤버 변수에 대한 더 자세한 정보를 컴파일러에 제공해야 합니다. 멤버의 왼쪽 괄호에 있는 모든 필드의 null-state를 설정하려면 컴파일러에 추가 정보가 필요합니다. 액세스 가능한 생성자는 개체를 초기화하는 데 사용할 수 있습니다. 멤버 필드를 null로 설정할 수 있는 경우 컴파일러는 각 메서드의 시작 부분에서 null-state가 maybe-null이라고 가정해야 합니다.
변수가 null 허용 참조 형식 또는 null을 허용하지 않는 참조 형식인지 선언할 수 있는 주석을 사용합니다. 이러한 주석은 변수의 null-state에 대한 중요한 문을 생성합니다.
-
참조는 null이 아니어야 합니다. null을 허용하지 않는 참조 변수의 기본 상태는 not-null입니다. 컴파일러는 null이 아닌지 먼저 확인하지 않고 이러한 참조를 역참조하기에 안전한지 확인하는 규칙을 적용합니다.
- 이 변수는 null이 아닌 값으로 초기화되어야 합니다.
- 이 변수에는
null값을 절대로 할당할 수 없습니다. 코드가 null이면 안 되는 변수에 maybe-null 식을 할당하면 컴파일러가 경고를 발생시킵니다.
-
참조가 null일 수 있습니다. null 허용 참조 변수의 기본 상태는 maybe-null입니다. 컴파일러는
null참조를 올바르게 확인하도록 규칙을 적용합니다.- 변수는 컴파일러가 값이
null않도록 보장할 수 있는 경우에만 역참조할 수 있습니다. - 이러한 변수는 기본
null값으로 초기화할 수 있으며 다른 코드에서null값을 할당할 수 있습니다. - 코드가 null일 수도 있는 변수에 maybe-null 식을 할당하면 컴파일러가 경고를 발생시키지 않습니다.
- 변수는 컴파일러가 값이
null을 허용하지 않는 참조 변수는 초기 null 상태가 null이 아닌 입니다. null을 허용하는 모든 참조 변수에는 초기 null-state인 maybe-null이 있습니다.
nullable 참조 형식은 nullable 값 형식과 동일한 구문을 사용하여 작성합니다. 변수 형식에 ?가 추가됩니다. 예를 들어 다음 변수 선언은 nullable 문자열 변수 name을 나타냅니다.
string? name;
null 허용 참조 형식이 사용하도록 설정되면 형식 이름에 ?이 추가되지 않은 모든 변수는 null을 허용하지 않는 참조 형식입니다. 이 기능을 사용하도록 설정하면 기존 코드의 모든 참조 형식 변수가 포함됩니다. 그러나 암시적 형식 지역 변수(var을 사용하여 선언됨)는 null 허용 참조 형식입니다. 이전 섹션에서 살펴본 것처럼 정적 분석은 지역 변수의 null-state를 확인하여 역참조하기 전에 해당 변수가 maybe-null인지 확인합니다.
때때로 변수가 null이 아님을 알 때에도 컴파일러가 변수의 null-state가 maybe-null이라고 판단할 수 있습니다. 이러한 경우, 경고를 무시해야 합니다. 변수 이름 뒤에 null-forgiving 연산자인 !를 사용하여 null-state를 not-null로 강제합니다. 예를 들어 name 변수가 null이 아닌 것으로 알고 있는데 컴파일러 경고가 발생하는 경우 다음 코드를 작성하여 컴파일러 분석을 재정의할 수 있습니다.
name!.Length;
null 허용 참조 형식 및 null 허용 값 형식은 유사한 의미 체계 개념을 제공합니다. 즉, 변수는 값이나 개체를 나타낼 수 있거나 해당 변수가 null일 수 있습니다. 그러나 null 허용 참조 형식과 null 허용 값 형식은 서로 다르게 구현됩니다. null 허용 값 형식은 System.Nullable<T>을 사용하여 구현되고, null 허용 참조 형식은 컴파일러에서 읽는 특성에 의해 구현됩니다. 예를 들어 string? 및 string은 둘 다 동일한 형식(System.String)으로 표현됩니다. 그러나 int? 및 int는 각각 System.Nullable<System.Int32> 및 System.Int32로 표현됩니다.
null 허용 참조 형식은 컴파일 시간 기능입니다. 즉, 호출자가 경고를 무시하고 의도적으로 null 허용이 아닌 참조를 기대하는 메서드에 대한 인수로 null을 사용할 수 있음을 의미합니다. 라이브러리 작성자는 null 인수 값에 대한 런타임 검사를 포함해야 합니다.
ArgumentNullException.ThrowIfNull은 런타임 시 null에 대해 매개 변수를 확인하는 데 기본 설정되는 옵션입니다. 또한 null 허용 주석을 사용하는 프로그램의 런타임 동작은 null 허용 주석(? 및 !)이 모두 제거된 경우 동일합니다. 유일한 목적은 디자인 의도를 표현하고 null 상태 분석에 대한 정보를 제공하는 것입니다.
중요합니다
nullable 주석을 사용하도록 설정하면 Entity Framework Core가 데이터 멤버가 필요한지 여부를 결정하는 방법이 변경될 수 있습니다. 자세한 내용은 Entity Framework Core Fundamentals: null 허용 참조 형식 작업 문서에서 알아볼 수 있습니다.
제네릭
제네릭에는 임의 형식 매개 변수 T?에 대한 T를 처리하는 자세한 규칙이 필요합니다. 기존의 역사와 null 허용 값 형식 및 null 허용 참조 형식의 서로 다른 구현으로 인해 규칙은 반드시 자세하게 설명됩니다.
Null 허용 값 형식은 System.Nullable<T> 구조체를 사용하여 구현됩니다.
Null 허용 참조 형식은 컴파일러에 의미 체계 규칙을 제공하는 형식 주석으로 구현됩니다.
-
T의 형식 인수가 참조 형식인 경우T?는 해당 null 허용 참조 형식을 참조합니다. 예를 들어,T가string이면T?는string?입니다. -
T의 형식 인수가 값 형식이면T?는 동일한 값 형식T를 참조합니다. 예를 들어,T가int이면T?도int입니다. -
T의 형식 인수가 null 허용 참조 형식인 경우T?는 동일한 null 허용 참조 형식을 참조합니다. 예를 들어,T가string?이면T?도string?입니다. -
T의 형식 인수가 null 허용 값 형식인 경우T?는 동일한 null 허용 값 형식을 참조합니다. 예를 들어,T가int?이면T?도int?입니다.
반환 값의 경우 T?는 [MaybeNull]T에 해당하고, 인수 값의 경우 T?는 [AllowNull]T에 해당합니다. 자세한 내용은 언어 참조의 null-state 분석을 위한 특성 문서를 참조하세요.
제약 조건을 사용하여 다른 동작을 지정할 수 있습니다.
-
class제약 조건은T가 null을 허용하지 않는 참조 형식(예:string)이어야 함을 의미합니다.string?에 대해T와 같은 null 허용 참조 형식을 사용하는 경우 컴파일러가 경고를 생성합니다. -
class?제약 조건은T가 null을 허용하지 않는 참조 형식(string) 또는 null 허용 참조 형식(예:string?)이어야 함을 의미합니다. 형식 매개 변수가string?와 같은 null 허용 참조 형식인 경우T?의 식은string?와 같은 동일한 null 허용 참조 형식을 참조합니다. -
notnull제약 조건은T가 null을 허용하지 않는 참조 형식 또는 null을 허용하지 않는 값 형식이어야 함을 의미합니다. 형식 매개 변수에 null 허용 참조 형식 또는 null 허용 값 형식을 사용하는 경우 컴파일러가 경고를 생성합니다. 또한T가 값 형식인 경우 반환 값은 해당 null 허용 값 형식이 아닌 이 값 형식입니다.
이러한 제약 조건은 T가 사용되는 방식에 대한 자세한 정보를 컴파일러에 제공하는 데 도움이 됩니다. 이는 개발자가 T의 형식을 선택할 때 도움이 되며 제네릭 형식의 인스턴스를 사용할 때 더 나은 null-state 분석을 제공합니다.
Nullable 컨텍스트
nullable 컨텍스트 nullable 참조 형식 주석이 처리되는 방법과 정적 null 상태 분석을 통해 생성되는 경고를 결정합니다. nullable 컨텍스트에는 주석 설정과 경고 설정의 두 가지 플래그가 포함됩니다.
주석 및 경고 설정은 모두 기존 프로젝트에 대해 기본적으로 사용하지 않도록 설정됩니다. .NET 6 (C# 10)부터 두 플래그는 새 프로젝트에서
소규모 프로젝트의 경우 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가 not-null입니다. - null forgiving 연산자인
!를 사용할 수 있습니다.
- 사용하도록 설정된 주석
: 코드가 역참조하거나 null을 허용하지 않는 변수에 null 식을 할당할 때 컴파일러는 경고를 내보내지 않습니다. - 모든 새로운 null 가능 경고가 비활성화됩니다.
-
?접미사를 사용하여 null 허용 참조 형식을 선언할 수 있습니다. -
?접미사가 없는 참조 형식 변수는 null을 허용하지 않는 참조 형식입니다. - null forgiving 연산자인
!를 사용할 수 있지만 영향은 없습니다.
<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 restore warnings: 경고 플래그를 프로젝트 설정으로 복원합니다. -
#nullable disable annotations: 주석 플래그를 설정하여 를 비활성화합니다. -
#nullable enable annotations: 주석 플래그를 설정하여을 사용 가능하도록 합니다. -
#nullable restore annotations: 주석 플래그를 프로젝트 설정으로 복원합니다.
모든 코드 줄에 대해 다음 조합 중 원하는 것을 설정할 수 있습니다.
| 경고 플래그 | 주석 플래그 | 사용하세요 |
|---|---|---|
| 프로젝트 기본값 | 프로젝트 기본값 | 기본값 |
| 활성화 | 비활성화 | 분석 경고 수정 |
| 활성화 | 프로젝트 기본값 | 분석 경고 수정 |
| 프로젝트 기본값 | 활성화 | 형식 주석 추가 |
| 활성화 | 활성화 | 이미 마이그레이션된 코드 |
| 비활성화 | 활성화 | 경고를 수정하기 전에 코드에 주석 달기 |
| 비활성화 | 비활성화 | 마이그레이션된 프로젝트에 레거시 코드 추가 |
| 프로젝트 기본값 | 비활성화 | 거의 없음 |
| 비활성화 | 프로젝트 기본값 | 거의 없음 |
이러한 9가지 조합은 컴파일러가 코드에 대해 내보내는 진단에 대한 세분화된 제어를 제공합니다. 아직 해결할 준비가 되지 않은 추가 경고를 표시하지 않고도 업데이트 중인 모든 영역에서 더 많은 기능을 사용하도록 설정할 수 있습니다.
중요합니다
전역 null 허용 컨텍스트는 생성된 코드 파일에 적용되지 않습니다. 두 전략 중 어느 것을 사용하든, 생성된 것으로 표시된 모든 소스 파일에 대해 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> 요소가 포함되어 있으며, 이 플래그는 활성화된로 설정됩니다.
이러한 옵션은 null 허용 참조 형식을 사용하도록 기존 코드베이스를 업데이트하는 두 가지 고유한 전략을 제공합니다.
알려진 함정
참조 형식을 포함한 배열 및 구조체는 nullable 참조와 null 안전성을 판단하는 정적 분석에서 알려진 문제점입니다. 두 경우 모두 null을 허용하지 않는 참조 형식이 경고를 생성하지 않고 null로 초기화될 수 있습니다.
구조체
null을 허용하지 않는 참조 형식을 포함하는 구조에서는 경고 없이 default를 할당할 수 있습니다. 다음 예제를 참조하세요.
using System;
#nullable enable
public struct Student
{
public string FirstName;
public string? MiddleName;
public string LastName;
}
public static class Program
{
public static void PrintStudent(Student student)
{
Console.WriteLine($"First name: {student.FirstName.ToUpper()}");
Console.WriteLine($"Middle name: {student.MiddleName?.ToUpper()}");
Console.WriteLine($"Last name: {student.LastName.ToUpper()}");
}
public static void Main() => PrintStudent(default);
}
앞의 예제에서 null을 허용하지 않는 참조 형식 PrintStudent(default) 및 FirstName이 null인 동안 LastName에는 경고가 없습니다.
또 다른 일반적인 사례는 일반 구조체를 처리하는 경우입니다. 다음 예제를 참조하세요.
#nullable enable
public struct S<T>
{
public T Prop { get; set; }
}
public static class Program
{
public static void Main()
{
string s = default(S<string>).Prop;
}
}
앞의 예에서 Prop 속성은 런타임에 null입니다. 경고 없이 null을 허용하지 않는 문자열에 할당됩니다.
배열
배열은 nullable 참조 형식의 알려진 문제가 되기도 합니다. 경고를 생성하지 않는 다음 예제를 고려하세요.
using System;
#nullable enable
public static class Program
{
public static void Main()
{
string[] values = new string[10];
string s = values[0];
Console.WriteLine(s.ToUpper());
}
}
앞의 예제에서 배열 선언은 해당 요소가 모두 null로 초기화되는 동안 null을 허용하지 않는 문자열을 보유함을 나타냅니다. 그런 다음, s 변수에는 null 값(배열의 첫 번째 요소)이 할당됩니다. 마지막으로 s 변수가 역참조되어 런타임 예외가 발생합니다.
생성자
해당 클래스의 생성자는 예외를 throw한 경우에도 여전히 클래스의 종료자를 호출합니다.
다음 예제에서는 해당 동작을 보여줍니다.
public class A
{
private string _name;
private B _b;
public A(string name)
{
ArgumentNullException.ThrowIfNullOrEmpty(name);
_name = name;
_b = new B();
}
~A()
{
Dispose();
}
public void Dispose()
{
_b.Dispose();
GC.SuppressFinalize(this);
}
}
public class B: IDisposable
{
public void Dispose() { }
}
public void Main()
{
var a = new A(string.Empty);
}
앞의 예제에서 매개 변수가 System.NullReferenceException인 경우, _b.Dispose();가 실행될 때 name가 throw됩니다. 생성자가 성공적으로 완료되면 호출 _b.Dispose(); 이 throw되지 않습니다. 그러나 정적 분석은 런타임 예외가 발생하지 않고 메서드(예: 생성자)가 완료되는지를 확인할 수 없기 때문에 컴파일러에서 경고가 발행되지 않습니다.
참고 항목
.NET