Как стать автором
Обновить

Комментарии 15

Что на счет повторной обработки исключения? И ретраев?
Например, у меня реализована какая-то пайплайна, которая умеет обрабатывать внутренние ексепшены, сохранять стейт и т.д. и т.п. Уровнем выше, скажем, уже на вью уровне, есть тоже какой-то простой глобальный обработчик, так вот как определить, что «ошибка вообще ошибка, что-то пошло плохо, звоните админам» или что «ну бывает, мы вот где-то там внутри ожидали такое и сами позаботимся». Скажем, валидно при тротлинге.

И на счет ретраев, хорошая ли практика добавлять в свои кастомные ексепшены поле типа «IsRetryable»? Опять же как пример — тротлинг со стороны БД или какого-то сервиса. И уровнем выше пытаться повторить операцию в зависимости от флага. Или же это уже логика консюмера и сам консюмер должен знать все ексепшены, которые надо ретраить?
Почти всегда предпочтительнее использовать второй пример.

Сейчас уже предпочтительнее использовать exception filters.

Это не всегда годится: иногда требуется частичная обработка исключения с пробросом его дальше.
Например, есть код типа такого:
public class DisposableClass : IDisposable
{
    public DisposableClass()
    {
        //Some initialization...
    }
    public SomeType Property { get; set; }
    public void Dispose()
    {
        //Do something...   
    }
}

public class SomeType { /*Some code*/}
class Someclass
{
    //... Some code
    public DisposableClass Method1()
    {
        DisposableClass result = new DisposableClass();
        try
        {
            result.Property = new SomeType();
        }
        catch 
        {
            result.Dispose(); //Надо выполнить очистку здесь, потому что больше негде.
            throw;
        }
        return result;
    }
}

То есть, метод создает объект типа, для которого нужна очистка (т.е. он реализует IDisposable) и сохраняет его в локальной переменной, и планирует его вернуть — но что-то пошло не так.
Если очистку здесь не выполнить, то ссылка на объект потеряется и он будет болтаться до сборки мусора, что, по-видимому, не согласуется с намерениями программиста.
Ну, или в самом первом примере автора статьи: он реализует особую обработку, а фильтр исключений не позволит выполнить вообще никакой обработки.
Ну, или в самом первом примере автора статьи: он реализует особую обработку, а фильтр исключений не позволит выполнить вообще никакой обработки.

Я там вижу только логирование. Сделать логирование на фильтрах исключений — не проблема.


Прямо скажем, я подозреваю, что я и disposal смогу на нем написать, но это уже не будет так хорошо читаться.

Логирование лично я тоже отношу к обработке, но это — вопрос терминологии, не более.
А вот делать что-то, имеющее побочный эффект, на фильтрах исключений (и вообще вставлять в условие код, имеющий побочный эффект) мне претит. И не только из-за того что это будет плохо читаться. А из-за немалого шанса нарваться, например: благодаря возможности неполного вычисления условия этот код после очередной неизбежной модификации может внезапно перестать в каких-то случаях выполняться.
А так, я тоже не вижу технических препятсвий такое сделать.
А из-за немалого шанса нарваться, например: благодаря возможности неполного вычисления условия этот код после очередной неизбежной модификации может внезапно перестать в каких-то случаях выполняться.

Если вы правильно напишете "условие" (а именно вынесете его в отдельную локальную функцию), вероятность такого события не выше, чем вероятность ошибки в catch.

А потом, чтобы далеко не лазить, кто-нибудь допишет к условию спереди какое-нибудь простое-простое дополнение через && или ||…
Не, нафиг-нафиг… Хотя, если привыкнуть всегда ожидать пакости — то можно, наверное.
А потом, чтобы далеко не лазить, кто-нибудь допишет к условию спереди какое-нибудь простое-простое дополнение через && или ||…

… а потом, чтобы далеко не лазить, кто-нибудь допишет условие к вашему catch. Того же уровня проблема.

Занятно, но зачем документацию работы с исключениями из официальной документации перепечатывать, тем более в урезанном виде?
И, обычно, стратегия обработки и работы с иключениями это часть дизайна решения. Почему бы сюда не включить «за» и «против» в предложеные стратегии? Например для «Может показаться очевидным, что нужно избегать исключений.» Почему? и т.д.
Согласен. Со статьями нужно работать, а не просто писать чтобы было.

По поводу исключений нужно начать статью с того факта, что исключения это крайне медленный механизм и всеми силами нужно их избегать. А то я видел проекты на C# где проверка бизнес логики была на исключениях…
По поводу исключений нужно начать статью с того факта, что исключения это крайне медленный механизм и всеми силами нужно их избегать.

Мы же можем скачать себе на компьютер код бенчмарка демонстрирующее такое поведение? Реализованного на разных языках и платформах?
Я помню разбор этот теста еще для C\C++ лет 20 назад, с обоснованиями. С тех пор ничего нового не видел для новых реализации CPU\OS\ и т.д.
Из последнего что встречал Коды ошибок — это гораздо медленнее, чем исключения
Я помню разбор этот теста еще для C\C++ лет 20 назад, с обоснованиями. С тех пор ничего нового не видел для новых реализации CPU\OS\ и т.д.

Отличие исключений в C++ от исключений в C# — в последнем случае сохраняется stacktrace, и это ОЧЕНЬ медленно. Поэтому исключения в C# стоит выбрасывать в действительно исключительных ситуациях, но не в штатных. Например, неправильный пользовательский ввод — это не исключение.


Из последнего что встречал Коды ошибок — это гораздо медленнее, чем исключения

Вы саму статью-то читали или только обратили внимание на жёлтый заголовок? Во-первых, речь о С++, а во-вторых, там сравнивается время работы кода в случае его успешного выполнения:


На 64-битных архитектурах включение использования исключений приводит к замедлению кода примерно на 1% в сравнении с кодом, который просто останавливается при возникновении ошибки. Коды ошибок, обычная альтернатива исключениям, используемая для обработки ошибок, после возникновения которых работу можно продолжить, снижают производительность примерно на 5%.


Ну да, 5% по сравнению с 1% выше в 5 раз. Вот только 5% — величина на уровне погрешности, для которой слово "гораздо" не применимо совершенно.

Отличие исключений в C++ от исключений в C# — в последнем случае сохраняется stacktrace, и это ОЧЕНЬ медленно.

Какие операции подразумеваются под «это»? и по сравнению с чем «ОЧЕНЬ медленно»?
Вы сравниваете С++ и .Net среду исполнения? или мы сравниваем внутри одного и того-же C++\С#\Java возврат кодов ошибок и исключений?
Если следовать спецификации «Common Language Infrastructure (CLI) Partition I Concepts and Architecture», то нет какого-то условия создавать и хранить стек вызовов или нет. Он есть всегда. т.е. накладные расходы «константа», отличие в накладных расходах на «protected block», но, опять же они тоже константа, потому что «finally» тоже часть фильтра входящая в «protected block». Исходя из того, что работа делается одна и та же не должно быть разницы возвращать результат через throw или return, за исключеним накладынх расходов исполнения throw. С учетом того, что throw это не нормальное явление и редкое, то им можно пренебречь, особенно на фоне обработки и логирования самого Exception.
Для С++ (давно не занимался) можно было при компиляции указать объем отладочной информации в исполняемом коде, и CaptureStackBackTrace (Captures a stack back trace by walking up the stack and recording the information for each frame.) часть Kernel32 Header: WinBase.h (include Windows.h) и получить стек вызова функции возможно.
12.4.2.5 Overview of exception handling

Each method in an executable has associated with it a (possibly empty) array of exception handling information. Each entry in the array describes a protected block, its filter, and its handler (which shall be a catch handler, a filter handler, a finally handler, or a fault· Protects a region including the current instruction pointer and handler). When an exception occurs, the CLI searches the array for the first protected block that
· Is a catch handler block and
· Whose filter wishes to handle the exception
If a match is not found in the current method, the calling method is searched, and so on. If no match is found the CLI will dump a stack trace and abort the program.
[Note: A debugger can intervene and treat this situation like a breakpoint, before performing any stack unwinding, so that the stack is still available for inspection through the debugger. end note]
If a match is found, the CLI walks the stack back to the point just located, but this time calling the finally and fault handlers. It then starts the corresponding exception handler. Stack frames are discarded either as this second walk occurs or after the handler completes, depending on information in the exception handler array entry associated with the handling block.
...
Protected regions, the type of the associated handler, and the location of the associated handler and (if needed) user-supplied filter code are described through an Exception Handler Table associated with each method.


Так что ответ на вопрос «есть ли тест который можно запустить и посмотреть результаты» для актуальных реализаций железа и ОС — интересен. То, что возможно придется получить ошибку предсказания исполнения с CPU в результате исключения и как результат просадку производительности — соглашусь. Останутся пенальти промаха предвыборки данных и не попадания в кеши процессора — возможно. Научится ли предсказатель предсказывать ветку «исключения» как наиболее вероятную в результате такой стратеги? Сохранятся ли кеши и предвыборка данных?
>Забавно вспоминать, как я был Java-программистом (когда .NET находился в стадии бета-тестирования). Мы создавали собственные пользовательские исключения для всего чего угодно. Возможно, это происходило из-за более явной реализации исключений в Java, но я не вижу этого в .NET и C#<
Потому что исключения в c# изначально были реализованы через механизм SEH в винде, который обладает следующей особенностью: он дешев для установки, но дорог для выполнения. А в Java исключения идут как дополнительный код ошибки, у каждого метода есть исключения который внешний код должен обработать
Почти всегда предпочтительнее использовать второй пример.
Мне больше нравится выкидывать новое исключение, оставляя исходное в InnerException.

Thread.CurrentPrincipal
Опасно с большой популярностью асинхронного кода. Возможно, лучше ClaimsPrincipal.Current, но с Principal я серьёзно не сталкивался. UPD нашёл в документации ms, что в asp.net core ClaimsPrincipal.Current тоже уже устарел и выброшен.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий