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);
}
从 C# 13 开始,此代码将生成编译器错误。
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
is
和 as
表达式始终返回 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
是一个 struct
,它定义 operator ==
和 operator !=
:
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();
}
若要修复此警告,请更改返回类型或删除方法。