Nullable 경고 해결

Tip

nullable 참조 형식이 새로운가요? 주석과 null 상태 분석을 이해하려면 먼저 nullable 참조 형식을 읽어보세요. 이 문서에서는 기능이 활성화된 프로젝트에서 경고가 표시된다고 가정합니다.

특정 컴파일러 오류 코드를 찾고 있나요? Nullable 경고 해결 참조 문서에서는 각 CS86xx 경고와 그에 맞는 해결 방법을 정리합니다.

nullable 참조 형식을 사용하도록 설정하면 컴파일러는 코드 동작이 주석과 일치하지 않는 모든 곳에서 경고를 발생합니다. 대부분의 경고는 작은 패턴 집합에 속합니다. 패턴을 인식하면 수정은 일반적으로 5가지 기술 중 하나입니다.

  • null 검사를 추가합니다.
  • ? 주석 또는 ! 주석을 추가하거나 제거합니다.
  • null 계약을 설명하는 특성을 추가합니다.
  • 변수를 올바르게 초기화합니다.
  • 프로젝트 설정을 확인합니다.

이 문서에서는 대표적인 예제를 사용하여 각 기술을 안내합니다. 목표는 경고를 침묵하는 것이 아닙니다. 컴파일러가 동일한 결론에 도달할 수 있도록 코드의 null 처리 의도를 명시적으로 만드는 것입니다.

Null 상태: 컴파일러가 추적하는 항목

기술을 살펴보기 전에 컴파일러가 잠재적인 null 상태 위반을 추적하는 방법을 파악하는 데 도움이 됩니다. 코드를 읽을 때 컴파일러는 각 식의 null 상태를 추적합니다. 식이 코드의 해당 지점에 있는지 null 여부에 대한 분석입니다. null 상태는 다음 두 값 중 하나입니다.

  • null이 아님 - 컴파일러는 식이 여기에 없음 null 을 증명할 수 있습니다. 검사 없이 안전하게 사용할 수 있습니다.
  • maybe-null - 컴파일러에서 배제 null할 수 없습니다. 식을 확인하지 않고 사용하면 경고가 생성됩니다.

컴파일러가 코드를 따를 때 변수의 null 상태가 변경됩니다. 반환 null 할 수 있는 메서드는 null일 수 있는 결과를 생성합니다. if (x is not null) 검사는 if 블록 내부에서 xnull이 아님으로 좁힙니다. 표시되는 경고는 컴파일러가 어떤 식이 null일 수 있는 상태라고 판단했으며, 이를 null이 아닌 것처럼 사용하려고 한다는 것을 알려 주는 것입니다. 이 문서의 나머지 부분에서 소개하는 각 기법은 식을 사용하기 전에 해당 식이 null이 아님을 보장하는 데 필요한 정보를 컴파일러에 제공하는 서로 다른 방법입니다.

null 검사 추가

가장 일반적인 경고는 null의 가능한 역참조입니다. 컴파일러는 변수의 null 상태를 어쩌면 null 로 추적하고 검사 없이 사용되는 변수를 확인했습니다.

public static int LengthOfMessageUnsafe(string? message)
{
    // Warning CS8602: dereference of a possibly null reference.
    return message.Length;
}

이 문제의 해결책은 대개 가드 절입니다. 가드 절은 입력이 유효하지 않은 경우 반환하거나 throw하는 메서드 또는 블록의 맨 위에 있는 검사입니다. 안전한 경로만 계속됩니다. 확인이 실행되면 컴파일러는 변수의 null 상태를 안전한 경로에서 null이 아닌 상태로 업데이트합니다.

public static int DereferenceFixed(string? message)
{
    if (message is null)
    {
        return 0;
    }

    // No warning: the compiler knows message is not-null on this path.
    return message.Length;
}

패턴 일치 (값의 셰이프를 테스트하는 식 is nullis { } 등) ????= null 검사를 포함합니다.

public static int NullOperatorsFix(string? message)
{
    // ?. evaluates to null if message is null; ?? supplies the fallback value.
    int length = message?.Length ?? 0;

    // Pattern matching narrows the type on the matching branch.
    if (message is { Length: > 0 })
    {
        length = message.Length;
    }

    return length;
}

속성 패턴 { Length: > 0 } 은 null이 아니message 해당 속성이 0보다 큰 경우에만 Length 일치하므로 컴파일러는 블록 내에서 messagenull이 아닌 것으로 처리됩니다if. 더 is not null 간단한 테스트는 속성을 검사하지 않고도 동일한 null 상태 축소를 생성합니다.

연산자에 대해 자세히 알아보려면 null 연산자를 참조하세요.

주석 조정

컴파일러는 또한 코드에서 maybe-null 식을 null을 허용하지 않는 변수에 할당하면 경고합니다. 이 경고는 다음 두 가지 중 하나를 의미합니다.

  • 변수는 null 값을 허용해야 합니다. 이 경우 형식에 a ? 를 추가합니다.
  • 이 식은 null 값을 반환하지 않습니다. 생성한 API에 주석을 추가합니다.
public static void AssignmentWarning()
{
    // Warning CS8600: converting null literal or possible null value to non-nullable type.
    string name = Lookup("nobody");
    Console.WriteLine(name);
}

합법적으로 null을 반환하는 경우 Lookup 누락된 값을 허용하도록 호출 사이트를 변경합니다.

public static void AssignmentFixed()
{
    string? name = Lookup("somebody");
    if (name is not null)
    {
        Console.WriteLine(name);
    }
}

null을 반환하지 않으면 Lookup null을 허용하지 않는 참조 형식을 반환하도록 해당 서명을 변경합니다. 반환된 값의 null 상태가 입력에 따라 달라지는 시나리오는 null 분석 특성에 대한 다음 섹션을 참조하세요.

값이 null이 아님을 보장할 수 있지만 그 보장을 타입 시스템으로 표현할 수 없을 때에만 null 허용 무시 연산자 !를 사용하세요. 각각 ! 은 컴파일러가 더 이상 사용자를 보호할 수 없는 위치이므로 검사를 추가하거나 원본 API에 주석을 추가하는 것을 선호합니다.

null 분석 특성 추가

경우에 따라 올바른 수정 사항이 통화 사이트에 없는 경우가 있습니다. 메서드의 서명은 입력과 출력 간의 관계를 정확하게 캡처하지 않으며 컴파일러는 안전하지 않은 코드 내에서 경고를 발생합니다.

public static bool IsPresent(string? text) =>
    !string.IsNullOrEmpty(text);

public static void CallerWithoutAttribute(string? text)
{
    if (IsPresent(text))
    {
        // Warning CS8602: dereference of a possibly null reference.
        // The signature doesn't tell the compiler text is not-null here.
        Console.WriteLine(text.Length);
    }
}

메서드가 반환IsPresent할 때 인수가 null이 아니라는 것을 증명하는 본문 true 이지만 서명은 그렇게 말하지 않습니다. Nullable 분석 특성을 추가하여 계약을 API의 일부로 만듭니다.

public static bool AttributedIsPresent([NotNullWhen(true)] string? text) =>
    !string.IsNullOrEmpty(text);

public static void CallerWithAttribute(string? text)
{
    if (AttributedIsPresent(text))
    {
        // No warning: the attribute tells the compiler text is not-null.
        Console.WriteLine(text.Length);
    }
}

일반적인 특성은 다음과 같습니다.

전체 목록은 Nullable 정적 분석 특성에 있습니다.

null을 허용하지 않는 멤버 초기화

생성자 경고는 null이 아닌 필드, 속성 또는 자동 속성 (예: public string Name { get; set; }컴파일러에서 생성된 백업 필드를 사용하는 속성)이 null이 아닌 값을 할당하지 않고 생성자를 종료한다는 것을 의미합니다.

public class PersonUninitialized
{
    // Warning CS8618: Non-nullable property 'Name' is uninitialized.
    public string Name { get; set; }
}

이 문제를 해결하는 방법에는 여러 가지가 있습니다. 디자인 의도와 가장 일치하는 항목을 선택합니다.

생성자 인수로 값을 요구합니다. 기본 생성자(형식 자체에 선언된 매개 변수, 본문 전체에서 사용 가능) 또는 속성을 초기화하는 일반 생성자를 사용합니다.

public class PersonInjected(string name)
{
    public string Name { get; } = name;
}

속성을 required만듭니다. 호출자는 객체 이니셜라이저를 통해 이를 초기화해야 합니다(new 뒤에 오는 { Property = value } 구문):

public class PersonRequired
{
    public required string Name { get; init; }
}

기본값으로 초기화합니다. 형식에 의미 있는 빈 값이 있는 경우 선언에서 초기화합니다.

public class PersonInitialized
{
    public string Name { get; set; } = "John Doe";
}

Tip

형식에 진정으로 양호한 기본값이 있는 경우에만 이 기술을 선택합니다. 호출자가 사용할 수 있는 유효한 완전한 기능의 인스턴스입니다. 예를 들어 빈 컬렉션이 있습니다. null을 대신하기 위해 sentinel(예: String.Empty, "N/A", "unknown" 또는 -1처럼 "값 없음"으로 취급하는 자리 표시자 값)을 만들어 내지 마세요. 그러면 경고를 숨길 뿐이고, 모든 호출자가 그 sentinel 값을 알고 직접 확인해야 하며, 타입 시스템은 아무 도움도 주지 못합니다. 좋은 기본값이 없으면 대신 속성을 null 허용으로 만듭니다.

속성을 null 허용으로 설정합니다. 값이 실제로 누락될 수 있는 경우 형식을 nullable로 변경합니다.

public class PersonOptional
{
    public string? Name { get; set; }
}

도우미 메서드가 멤버를 초기화하는 경우, 컴파일러가 해당 메서드 호출을 인정할 수 있도록 도우미 메서드에 MemberNotNullAttribute 특성을 지정합니다.

프로젝트 설정 확인

새 C# 프로젝트는 기본적으로 nullable 참조 형식을 사용하도록 설정하므로 작성하거나 읽은 대부분의 코드에는 이미 기능이 켜져 있습니다. 일반적으로 아무 것도 구성할 필요가 없습니다. 프로젝트에서 해당 기능이 활성화되어 있는지 궁금하거나 설정을 변경해야 하는 경우 .csproj에서 <Nullable> 요소를 찾으세요:

<PropertyGroup>
  <Nullable>enable</Nullable>
</PropertyGroup>

지원되는 일반적인 값은 enable (새 프로젝트의 기본값) 및 disable. 요소가 누락된 경우 프로젝트는 SDK 및 대상 프레임워크 집합의 기본값을 사용합니다.

지시문이 있는 파일 #nullable 의 일부에 대해서만 nullable을 사용하도록 설정하거나 기존 코드베이스를 마이그레이션할 때 부분 warningsannotations 모드를 사용해야 하는 경우 Nullable 마이그레이션 전략을 참조하세요.

다음 단계로 이동할 위치

경고가 이러한 패턴 중 어느 것에도 맞지 않는 경우, nullable 경고 해결 참조 문서에는 컴파일러에서 발생시키는 모든 CS86xx 경고에 대한 해결 방법이 나와 있습니다.

기존 코드베이스에서 nullable 참조 형식을 점진적으로 사용하도록 설정하는 마이그레이션을 계획하려면 Nullable 마이그레이션 전략을 참조하세요.