C# 경고 웨이브

C# 컴파일러의 각 릴리스에는 새로운 경고 및 오류가 도입될 수 있습니다. 기존 코드에 대해 새로운 경고가 보고될 수 있는 경우 해당 경고는 경고 웨이브라고 하는 옵트인 시스템에 도입됩니다. 옵트인 시스템은 이를 사용하도록 설정하기 위한 조치를 취하지 않으면 기존 코드에 대한 새로운 경고가 표시되어서는 안 된다는 것을 의미합니다. 경고 웨이브는 프로젝트 파일의 AnalyticLevel 요소를 사용하여 사용하도록 설정됩니다. <TreatWarningsAsErrors>true</TreatWarningsAsErrors>가 지정된 경우 사용하도록 설정된 경고 웨이브 경고는 오류를 생성합니다. 경고 웨이브 5 진단이 C# 9에 추가되었습니다. 경고 웨이브 6 진단이 C# 10에 추가되었습니다. 경고 웨이브 7 진단이 C# 11에 추가되었습니다. 경고 웨이브 8 진단이 C# 12에 추가되었습니다.

CS9123 - 비동기 메서드에서 로컬 또는 매개 변수의 주소를 가져오면 GC 홀이 만들어질 수 있습니다.

경고 웨이브 8

비동기 메서드의 매개 변수나 지역 변수에 & 연산자를 사용하면 안 됩니다. 다음 코드는 CS9123을 생성합니다.

public static async Task LogValue()
{
    int x = 1;
    unsafe {
        int* y = &x;
        Console.WriteLine(*y);
    }
    await Task.Delay(1000);
}

CS8981 - 형식 이름에는 소문자 ASCII 문자만 포함됩니다.

경고 웨이브 7

C#에 추가된 새 키워드는 모두 소문자 ASCII 문자입니다. 이 경고는 사용자의 형식이 향후 키워드와 충돌하지 않도록 보장합니다. 다음 코드는 CS8981을 생성합니다.

public class lowercasename
{
}

대문자, 숫자, 밑줄 등 소문자 이외의 ASCII 문자를 하나 이상 포함하도록 형식 이름을 변경하여 이 경고를 해결할 수 있습니다.

CS8826 - partial 메서드 선언에는 서명 차이가 있습니다.

경고 웨이브 6

이 경고는 partial 메서드 서명 간의 차이점을 보고할 때 발생하는 일부 불일치를 수정합니다. partial 메서드 서명이 다른 CLR 서명을 만들면 컴파일러는 항상 오류를 보고했습니다. 이제 컴파일러는 서명이 구문적으로 다른 C#인 경우 CS8826을 보고합니다. 다음 partial 클래스를 고려합니다.

public partial class PartialType
{
    public partial void M1(int x);

    public partial T M2<T>(string s) where T : struct;

    public partial void M3(string s);


    public partial void M4(object o);
    public partial void M5(dynamic o);
    public partial void M6(string? s);
}

다음 partial 클래스 구현은 CS8626의 여러 예를 생성합니다.

public partial class PartialType
{
    // Different parameter names:
    public partial void M1(int y) { }

    // Different type parameter names:
    public partial TResult M2<TResult>(string s) where TResult : struct => default;

    // Relaxed nullability
    public partial void M3(string? s) { }


    // Mixing object and dynamic
    public partial void M4(dynamic o) { }

    // Mixing object and dynamic
    public partial void M5(object o) { }

    // Note: This generates CS8611 (nullability mismatch) not CS8826
    public partial void M6(string s) { }
}

참고 항목

다른 선언이 null 허용 참조 형식을 허용하는 경우 메서드 구현에서 null을 허용하지 않는 참조 형식을 사용하는 경우 CS8826 대신 CS8611이 생성됩니다.

이러한 경고의 인스턴스를 수정하려면 두 서명이 일치하는지 확인합니다.

CS7023 - 'is' 또는 'as' 식에 정적 형식이 사용됩니다.

경고 단계 5

정적 형식의 인스턴스를 만들 수 없기 때문에 isas 식은 항상 정적 형식에 대해 false를 반환합니다. 다음 코드는 CS7023을 생성합니다.

static class StaticClass
{
    public static void Thing() { }
}

void M(object o)
{
    // warning: cannot use a static type in 'is' or 'as'
    if (o is StaticClass)
    {
        Console.WriteLine("Can't happen");
    }
    else
    {
        Console.WriteLine("o is not an instance of a static class");
    }
}

형식 테스트는 결코 성공할 수 없기 때문에 컴파일러는 이 경고를 보고합니다. 이 경고를 수정하려면 테스트를 제거하고 테스트가 성공한 경우에만 실행되는 모든 코드를 제거합니다. 앞의 예에서는 else 절이 항상 실행됩니다. 메서드 본문은 다음 한 줄로 바뀔 수 있습니다.

Console.WriteLine("o is not an instance of a static class");

CS8073 - 식의 결과는 항상 'false'(또는 'true')입니다.

경고 단계 5

==!= 연산자는 struct 형식의 인스턴스를 null과 비교할 때 항상 false(또는 true)를 반환합니다. 다음 코드는 이 경고를 보여 줍니다. Soperator ==operator !=를 정의한 struct라고 가정합니다.

class Program
{
    public static void M(S s)
    {
        if (s == null) { } // CS8073: The result of the expression is always 'false'
        if (s != null) { } // CS8073: The result of the expression is always 'true'
    }
}

struct S
{
    public static bool operator ==(S s1, S s2) => s1.Equals(s2);
    public static bool operator !=(S s1, S s2) => !s1.Equals(s2);
    public override bool Equals(object? other)
    {
        // Implementation elided
        return false;
    }
    public override int GetHashCode() => 0;

    // Other details elided...
}

이 오류를 수정하려면 개체가 null인 경우 실행되는 Null 검사와 코드를 제거합니다.

CS8848 - 우선 순위로 인해 여기서 'from' 연산자를 사용할 수 없습니다. 명확하게 하려면 괄호를 사용합니다.

경고 단계 5

다음 예에서는 이 경고를 보여 줍니다. 연산자의 우선 순위로 인해 식이 잘못 바인딩되었습니다.

bool b = true;
var source = new Src();
b = true;
source = new Src();
var a = b && from c in source select c;
Console.WriteLine(a);

var indexes = new Src2();
int[] array = { 1, 2, 3, 4, 5, 6, 7 };
var range = array[0..from c in indexes select c];

이 오류를 해결하려면 쿼리 식 주위에 괄호를 넣으세요.

bool b = true;
var source = new Src();
b = true;
source = new Src();
var a = b && (from c in source select c);
Console.WriteLine(a);

var indexes = new Src2();
int[] array = { 1, 2, 3, 4, 5, 6, 7 };
var range = array[0..(from c in indexes select c)];

멤버는 완전히 할당되어야 하며 할당되지 않은 변수를 사용해야 합니다(CS8880, CS8881, CS8882, CS8883, CS8884, CS8885, CS8886, CS8887).

경고 단계 5

여러 경고는 가져온 어셈블리에 선언된 struct 형식에 대한 명확한 할당 분석을 개선합니다. 다음 예와 같이 가져온 어셈블리의 구조체에 참조 형식의 액세스할 수 없는 필드(일반적으로 private 필드)가 포함된 경우 이러한 새로운 경고가 모두 생성됩니다.

public struct Struct
{
    private string data = String.Empty;
    public Struct() { }
}

다음 예에서는 개선된 한정 할당 분석에서 생성된 경고를 보여 줍니다.

  • CS8880: 제어가 호출자에게 반환되기 전에 자동 구현 속성 'Property'를 완전히 할당해야 합니다.
  • CS8881: 제어권이 호출자에게 반환되기 전에 'field' 필드를 완전히 할당해야 합니다.
  • CS8882: 제어가 현재 메서드를 벗어나기 전에 'parameter' out 매개 변수를 할당해야 합니다.
  • CS8883: 할당되지 않은 자동 구현 속성 'Property'를 사용합니다.
  • CS8884: 할당되지 않은 필드 'Field'를 사용합니다.
  • CS8885: 모든 필드가 할당되기 전에는 'this' 개체를 사용할 수 없습니다.
  • CS8886: 할당되지 않은 출력 매개 변수 'parameterName'을 사용합니다.
  • CS8887: 할당되지 않은 지역 변수 'variableName' 사용합니다.
public struct DefiniteAssignmentWarnings
{
    // CS8880
    public Struct Property { get; }
    // CS8881
    private Struct field;

    // CS8882
    public void Method(out Struct s)
    {

    }

    public DefiniteAssignmentWarnings(int dummy)
    {
        // CS8883
        Struct v2 = Property;
        // CS8884
        Struct v3 = field;
        // CS8885:
        DefiniteAssignmentWarnings p2 = this;
    }

    public static void Method2(out Struct s1)
    {
        // CS8886
        var s2 = s1;
        s1 = default;
    }

    public static void UseLocalStruct()
    {
        Struct r1;
        var r2 = r1;
    }
}

가져온 구조체를 기본값으로 초기화하거나 할당하여 이러한 경고를 수정할 수 있습니다.

public struct DefiniteAssignmentNoWarnings
{
    // CS8880
    public Struct Property { get; } = default;
    // CS8881
    private Struct field = default;

    // CS8882
    public void Method(out Struct s)
    {
        s = default;
    }

    public DefiniteAssignmentNoWarnings(int dummy)
    {
        // CS8883
        Struct v2 = Property;
        // CS8884
        Struct v3 = field;
        // CS8885:
        DefiniteAssignmentNoWarnings p2 = this;
    }

    public static void Method2(out Struct s1)
    {
        // CS8886
        s1 = default;
        var s2 = s1;
    }

    public static void UseLocalStruct()
    {
        Struct r1 = default;
        var r2 = r1;
    }
}

CS8892 - 동기 진입점 ‘method’를 찾았으므로 메서드가 진입점으로 사용되지 않습니다.

경고 단계 5

이 경고는 하나 이상의 동기 진입점을 포함하여 유효한 진입점이 여러 개인 경우 모든 비동기 진입점 후보에 대해 생성됩니다.

다음 예에서는 CS8892를 생성합니다.

public static void Main()
{
    RunProgram();
}

// CS8892
public static async Task Main(string[] args)
{
    await RunProgramAsync();
}

참고 항목

컴파일러는 항상 동기 진입점을 사용합니다. 동기 진입점이 여러 개인 경우 컴파일러 오류가 발생합니다.

이 경고를 해결하려면 비동기 진입점을 제거하거나 이름을 바꿉니다.

CS8897 - 정적 형식을 매개 변수로 사용할 수 없습니다.

경고 단계 5

인터페이스 멤버는 형식이 정적 클래스인 매개 변수를 선언할 수 없습니다. 다음 코드는 CS8897과 CS8898을 모두 보여 줍니다.

public static class Utilities
{
    // elided
}

public interface IUtility
{
    // CS8897
    public void SetUtility(Utilities u);

    // CS8898
    public Utilities GetUtility();
}

이 경고를 해결하려면 매개 변수 형식을 변경하거나 메서드를 제거합니다.

CS8898 - 정적 형식을 반환 형식으로 사용할 수 없습니다.

경고 단계 5

인터페이스 멤버는 정적 클래스인 반환 형식을 선언할 수 없습니다. 다음 코드는 CS8897과 CS8898을 모두 보여 줍니다.

public static class Utilities
{
    // elided
}

public interface IUtility
{
    // CS8897
    public void SetUtility(Utilities u);

    // CS8898
    public Utilities GetUtility();
}

이 경고를 해결하려면 반환 형식을 변경하거나 메서드를 제거합니다.