День двести семьдесят первый. #BestPractices

Разработка Исключений

Исключения и Производительность

Одна общая проблема, связанная с исключениями, заключается в том, что, если исключения используются для кода, который обычно завершается неудачей, производительность кода будет неприемлемой. Это реальная проблема. Когда член класса выбрасывает исключение, его производительность может упасть на несколько порядков. Тем не менее, можно добиться хорошей производительности, строго придерживаясь правил обработки исключений, запрещающих использование кодов ошибок.



Паттерн Тестер-Исполнитель (Tester-Doer)

Иногда производительность элемента, генерирующего исключение, можно улучшить, разбив его на две части. Рассмотрим на метод Add интерфейса ICollection<T>:

ICollection<int> numbers = …

numbers.Add(1);

Метод Add выбрасывает исключение, если коллекция доступна только для чтения. Это может быть проблемой производительности в сценариях, где ожидается, что вызов метода, часто будет заканчиваться неудачей. Один из способов сгладить проблему - проверять, доступна ли коллекция для записи, прежде чем добавлять значение:

ICollection<int> numbers = …



if (!numbers.IsReadOnly)

numbers.Add(1);

Член класса, используемый для проверки условия (в нашем примере свойство IsReadOnly), называется тестером. Член, используемый для выполнения операции, потенциально приводящей к исключению (в нашем примере метод Add), называется исполнителем.



⚠️ РАССМОТРИТЕ использование паттерна Tester-Doer для членов класса, которые могут генерировать исключения в распространённых сценариях, чтобы избежать проблем производительности, связанных с исключениями.



Паттерн Try-Parse

Для API, чрезвычайно чувствительных к производительности, можно использовать еще более быстрый паттерн Try-Parse. Он требует изменения имени члена, чтобы оно отражало суть того, что он будет делать. Например, DateTime определяет метод Parse, который выбрасывает исключение в случае сбоя парсинга строки. И метод TryParse, который пытается выполнить парсинг, но возвращает false, если он завешается неудачей, а в случае успеха возвращает результат в out параметре.

public struct DateTime

{

public static DateTime Parse(string dateTime) { … }

public static bool TryParse(string dateTime, out DateTime result) { … }

}

При использовании этого паттерна важно строго определить функциональность блока try. Если в методе произойдёт сбой по какой-либо иной причине, кроме определённой в блоке try, метод всё равно должен выбросить соответствующее исключение.



⚠️ РАССМОТРИТЕ использование паттерна Try-Parse для членов, которые могут генерировать исключения в распространённых сценариях, чтобы избежать проблем производительности, связанных с исключениями.

ИСПОЛЬЗУЙТЕ префикс «Try» и логический тип возвращаемого значения для методов, реализующих этот паттерн.

❗️ НЕОБХОДИМО добавить и обычный метод, генерирующий исключение, для каждого метода, использующего паттерн Try-Parse.



Источник: https://docs.microsoft.com/en-us/dotnet/standard/design-guidelines/