C# 警告波

C# 编译器的每个版本中可能会引入新的警告和错误。 如果现有代码上可以报告新的警告,则会在称为警告波的选择加入系统下引入这些警告。 选择加入系统意味着,如果不采取操作启用现有代码,就不应在其中看到新的警告。 使用项目文件中的 AnalysisLevel 元素启用警告波。 指定 <TreatWarningsAsErrors>true</TreatWarningsAsErrors> 后,启用的警告波警告将生成错误。 C# 9 中添加了警告波 5 诊断。 C# 10 中添加了警告波 6 诊断。 C# 11 中添加了警告波 7 诊断。 C# 12 中添加了警告波 8 诊断。

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 - 分部方法声明具有签名差异。

警告波 6

此警告修正了报告分部方法签名之间差异时的一些不一致。 当分部方法签名创建不同的 CLR 签名时,编译器始终报告错误。 现在,当签名为在语法上有差异的 C# 时,编译器会报告 CS8826。 考虑以下分部类:

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);
}

以下分部类实现生成多个 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 的引用类型,则生成 CS8611,而不是 CS8826。

若要修复这些警告的任何实例,请确保两个签名匹配。

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)。 下面的代码对此警告进行了演示。 假设 S 是已定义 operator ==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:在控件离开当前方法之前,必须对 out 参数“parameter”赋值。
  • 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();
}

若要修复此警告,请更改返回类型或删除方法。