Поделиться через


CA1810: инициализируйте статические поля ссылочного типа встроенными средствами

Свойство Значение
Идентификатор правила CA1810
Заголовок Инициализируйте статические поля ссылочных типов при объявлении
Категория Производительность
Исправление является критическим или не критическим Не критическое
Включен по умолчанию в .NET 8 No

Причина

Ссылочный тип объявляет явный статический конструктор.

Описание правила

Если в типе объявляется явный статический конструктор, компилятор JIT добавляет проверку в каждый статический метод и конструктор экземпляров этого типа, чтобы убедиться, что статический конструктор уже вызывался ранее. Статическая инициализация активируется при доступе к любому статическому элементу или при создании экземпляра типа. Однако статическая инициализация не запускается, если объявить переменную типа, но не использовать ее, что может быть важно, если инициализация меняет глобальное состояние.

Если все статические данные инициализированы встроенными и явным статическим конструктором не объявлены, компиляторы общего промежуточного языка (CIL) добавляют beforefieldinit флаг и неявный статический конструктор, который инициализирует статические данные в определение типа CIL. Когда JIT-компилятор обнаруживает флаг beforefieldinit, в большинстве случаев проверки статических конструкторов не добавляются. Статическая инициализация будет гарантированно выполняться в определенный момент перед обращением к статическим полям, но не до вызова статического метода или конструктора экземпляра. Обратите внимание, что статическая инициализация может произойти в любое время после объявления переменной типа.

Проверки статических конструкторов могут привести к снижению производительности. Часто статический конструктор используется только для инициализации статических полей. В этом случае необходимо лишь убедиться, что статическая инициализация выполняется перед первым доступом к статическому полю. Поведение beforefieldinit подходит для этих и большинства других типов. Оно неприемлемо, только если статическая инициализация влияет на глобальное состояние, и одно из следующих условий верно:

  • Воздействие на глобальное состояние является дорогостоящим и не требуется, если тип не используется.

  • Доступ к эффектам глобального состояния возможен без доступа к любым статическим полям типа.

Устранение нарушений

Чтобы устранить нарушение данного правила, выполните инициализацию всех статических данных при их объявлении и удалите статический конструктор.

Когда лучше отключить предупреждения

Если применяется одно из следующих действий, можно отключить предупреждение из этого правила:

  • Производительность не является проблемой.
  • Глобальные изменения состояния, вызванные статической инициализацией, являются дорогостоящими или должны быть гарантированы до вызова статического метода типа или создания экземпляра типа.

Отключение предупреждений

Если вы просто хотите отключить одно нарушение, добавьте директивы препроцессора в исходный файл, чтобы отключить и повторно включить правило.

#pragma warning disable CA1810
// The code that's violating the rule is on this line.
#pragma warning restore CA1810

Чтобы отключить правило для файла, папки или проекта, задайте его серьезность none в файле конфигурации.

[*.{cs,vb}]
dotnet_diagnostic.CA1810.severity = none

Дополнительные сведения см. в разделе Практическое руководство. Скрытие предупреждений анализа кода.

Пример

В следующем примере показаны тип StaticConstructor, который нарушает правило, и тип NoStaticConstructor, который заменяет статический конструктор встроенной инициализацией для выполнения этого правила.

public class StaticConstructor
{
    static int someInteger;
    static string? resourceString;

    static StaticConstructor()
    {
        someInteger = 3;
        ResourceManager stringManager =
           new ResourceManager("strings", Assembly.GetExecutingAssembly());
        resourceString = stringManager.GetString("string");
    }

    public void Print()
    {
        Console.WriteLine(someInteger);
    }
}

public class NoStaticConstructor
{
    static int someInteger = 3;
    static string? resourceString = InitializeResourceString();

    static string? InitializeResourceString()
    {
        ResourceManager stringManager =
           new ResourceManager("strings", Assembly.GetExecutingAssembly());
        return stringManager.GetString("string");
    }

    public void Print()
    {
        Console.WriteLine(someInteger);
    }
}
Imports System
Imports System.Resources

Namespace ca1810

    Public Class StaticConstructor

        Shared someInteger As Integer
        Shared resourceString As String

        Shared Sub New()

            someInteger = 3
            Dim stringManager As New ResourceManager("strings",
            System.Reflection.Assembly.GetExecutingAssembly())
            resourceString = stringManager.GetString("string")

        End Sub

    End Class


    Public Class NoStaticConstructor

        Shared someInteger As Integer = 3
        Shared resourceString As String = InitializeResourceString()

        Private Shared Function InitializeResourceString()

            Dim stringManager As New ResourceManager("strings",
            System.Reflection.Assembly.GetExecutingAssembly())
            Return stringManager.GetString("string")

        End Function

    End Class

End Namespace

Обратите внимание на добавление флага beforefieldinit в определение CIL для NoStaticConstructor класса.

.class public auto ansi StaticConstructor
extends [mscorlib]System.Object
{
} // end of class StaticConstructor

.class public auto ansi beforefieldinit NoStaticConstructor
extends [mscorlib]System.Object
{
} // end of class NoStaticConstructor