Обложка канала

.NET Разработчик

Опытный разработчик не так давно зашёл в .Net и поставил цель получить сертификат Microsoft. Свой ежедневный прогресс он описывает на канале .Net Разработчик. Заметки об изученном материале, советы по повышению производительности и поддержке мотивации, ин

.NET Разработчик

4 года назад
Открыть в
День 1183. #ЗаметкиНаПолях Преимущества в Производительности Запечатанных Классов в .NET По умолчанию классы не запечатаны. Это означает, что вы можете наследовать от них. Это неправильное поведение по умолчанию. Если класс не предназначен для наследования, он должен быть запечатан. Вы можете изменить это позже, при необходимости. Кроме того, это влияет на производительность. Когда класс запечатан, JIT может применить оптимизацию и немного повысить производительность приложения. Вызов виртуальных методов При вызове виртуальных методов фактический метод находится во время выполнения на основе фактического типа объекта. Каждый тип имеет таблицу виртуальных методов (vtable), которая содержит адреса всех виртуальных методов. Эти указатели используются во время выполнения для вызова соответствующих реализаций (динамическая диспетчеризация). Если JIT знает фактический тип объекта, он может пропустить виртуальную таблицу и напрямую вызвать правильный метод. Использование запечатанных типов помогает JIT в этом.
public class SealedBenchmark
{
  NonSealedType nonSealedType = new();
  SealedType sealedType = new();

  public void CallMethod_NonSealed()
   => nonSealedType.Method();

  public void CallMethod_Sealed() 
    => sealedType.Method();
}

Здесь JIT не может знать тип nonSealedType. Он мог быть установлен в производный тип другим методом. Поэтому должен быть виртуальный вызов. В отличие от следующего кода:
var instance = new NonSealedType();
instance.Method();
Здесь JIT точно знает тип, поэтому делает прямой вызов. Приведение типов (is / as) При приведении типов среда выполнения должна проверять тип объекта во время выполнения. При приведении к незапечатанному типу среда выполнения должна проверять наличие всех типов в иерархии. Однако при приведении к запечатанному типу среда выполнения должна проверять только тип объекта, поэтому оно выполняется быстрее. Массивы Массивы в .NET ковариантны. Это означает, что допустимо
BaseType[] value = new DerivedType[1];

Ковариантность приводит к снижению производительности. JIT должен проверять тип объекта перед назначением элемента в массив. При использовании запечатанных типов JIT может удалить проверку. Преобразование массивов в Span<T> Вы можете преобразовать массивы в Span<T> или ReadOnlySpan<T>. По тем же причинам, что и в предыдущем разделе, JIT должен проверить тип объекта перед преобразованием массива в Span<T>. При использовании запечатанного типа можно избежать проверки. Результаты теста быстродействия на картинке ниже. Обнаружение недостижимого кода При использовании запечатанного типа компилятор знает, что некоторые преобразования недействительны. Таким образом, он может сообщать о предупреждениях и ошибках. Это поможет уменьшить количество ошибок в вашем приложении, а также удалить недостижимый код.
class Sample
{
  public void Foo(NonSealedType obj)
  {
    _ = obj as IMyInterface;
    // ОК, т.к. производные класса 
    // могут реализовывать интерфейс
  }

  public void Foo(SealedType obj)
  {
    _ = obj is IMyInterface; 
    // ⚠️ Предупреждение CS0184

    _ = obj as IMyInterface;
    // ❌ Ошибка CS0039
  }
}

Источник: www.meziantou.net/perform…lass.htm