Волны предупреждений C#

Новые предупреждения и ошибки можно ввести в каждом выпуске компилятора C#. Когда могут быть сообщены новые предупреждения по существующему коду, эти предупреждения вводятся в системе подписки, называемой волной предупреждений. Система согласия означает, что вы не должны видеть новые предупреждения в существующем коде, не выполняя действия, чтобы включить их. Если задано <TreatWarningsAsErrors>true</TreatWarningsAsErrors>, включенные предупреждения волны генерируют ошибки. Предупреждения пятой волны диагностики были добавлены в C# 9. Предупреждения волны 6 были добавлены в C# 10. Добавлены диагностика предупреждений волны 7 в C# 11. Диагностика предупреждений восьмой волны была добавлена в C# 12. В C# 13 добавлена диагностика волны предупреждений 9. В C# 14 добавлена диагностика волны предупреждений 10.

Начиная с пакета SDK для .NET 7 (C# 11), система сборки задает волны предупреждений со следующими правилами:

  • AnalysisLevel отслеживает текущий TFM, если он не указан
  • Уровень анализа присваивается значение latest, если текущий TFM является "последним" TFM (как определено свойством, которое нам нужно обновить).
  • WarningLevel должен отслеживать текущий TFM, если он не указан
  • WarningLevel не должен переопределить предоставленное пользователем значение
  • WarningLevel должен иметь значение 4, если проект является проектом .NET Framework

Для версий SDK, предшествующих .NET 7, AnalysisLevel всегда перезаписывал WarningLevel.

CS9265 — полю никогда не присваивается ссылка и всегда будет иметь значение по умолчанию.

Предупреждение волны 10

Поле ref в ref struct, которому никогда не присваивается ref, всегда имеет значение по умолчанию, которое является null-ссылкой. Следующий код создает CS9265:

ref struct Container
{
    // CS9265: Field 'value' is never ref-assigned to,
    // and will always have its default value (null reference)
    public ref int value;
}

Чтобы устранить это предупреждение, назначьте поле в инициализаторе полей или на всех путях кода конструктора. Кроме того, удалите ref модификатор из объявления поля, если поле не должно быть ссылкой.

CS9123 — оператор '&' не должен использоваться для параметров или локальных переменных в асинхронных методах

Предупреждение волна 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. Теперь компилятор сообщает CS8826, когда подписи синтаксически отличаются от C#. Рассмотрим следующий частичный класс:

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, создается 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 — результат выражения всегда является 'значением', так как значение типа 'type' никогда не равно 'null' этого же типа.

Предупреждающая волна 5

Операторы == и != всегда возвращают false (или true) при сравнении экземпляра типа struct с null. В следующем коде показано это предупреждение. Предположим, что 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, CS886, CS8887)

Предупреждение, волна 5

Несколько предупреждений улучшают определенный анализ назначений для типов, struct объявленных в импортированных сборках. Все эти новые предупреждения создаются, когда структура импортированной сборки включает недоступное поле (обычно private поле) ссылочного типа, как показано в следующем примере:

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

В следующих примерах показаны предупреждения, созданные из улучшенного анализа определенного назначения:

  • CS8880: Автоматически реализованное свойство 'Property' должно быть полностью присвоено до возврата управления вызывающему объекту. Рассмотрите возможность обновления до языковой версии "версия" для автоматической установки значения по умолчанию для свойства.
  • CS8881: Поле `field` должно быть полностью инициализировано перед возвратом управления вызывающему объекту. Попробуйте обновить версию языка до версии "версия", чтобы автоматически установить значение по умолчанию.
  • CS8882: параметр out 'parameter' должен быть присвоен перед выходом из текущего метода.
  • CS8883: использование, возможно, неназначенных автоматически реализованных свойств "Property".
  • CS8884: использование, возможно, неназначаемого поля "Поле"
  • CS8885: объект "этот" нельзя использовать до назначения всех его полей. Рекомендуется обновить язык до версии "версия", чтобы автоматически назначать значения по умолчанию для неназначенных полей.
  • 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();
}

Чтобы устранить это предупреждение, измените возвращаемый тип или удалите метод.