В чем преимущество использования фильтров исключений и когда их следует использовать?

Сравнивая старый способ и новый способ обработки ошибок с использованием фильтров исключений, в чем именно для меня преимущество использования фильтров и когда мне следует его использовать? есть ли сценарий, в котором я могу получить хорошее преимущество этой новой функции?

Я читал о раскручивании стека, но до сих пор не понимаю сценария, в котором мы не можем справиться с этим по-старому. Объясните, как мне 5, пожалуйста.

try
{
    Foo.DoSomethingThatMightFail(null);
}
catch (MyException ex) when (ex.Code == 42)
{
    Console.WriteLine("Error 42 occurred");
}

vs

try
{
    Foo.DoSomethingThatMightFail(null);
}
catch (MyException ex)
{
    if (ex.Code == 42)
        Console.WriteLine("Error 42 occurred");
    else
        throw;
}

Я знаю, что есть другая версия этого вопроса, проблема в том, что в вопросе упоминаются преимущества, которые я не могу найти, например.

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

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

class specialException : Exception
{
   public DateTime sentDateTime { get; } = DateTime.Now;
   public int code { get; } = 0;
   public string emailsToAlert { get; } = "[email protected]";
}

тогда:

try
{
   throw new specialException();
   //throw new Exception("Weird exception");
   //int a = Int32.Parse("fail");
}
catch (specialException e) when(e.code == 0)
        {
            WriteLine("E.code 0");
            throw;
            //throw e;
        }
catch (FormatException e) 
        {
            if (cond1)
            {
                WriteLine("cond1 " + e.GetBaseException().Message+" - "+e.StackTrace);
                throw;
            }
            throw;
        }
catch (Exception e) //when (cond2)
        {
            Console.WriteLine("cond2! " + e.Message);
            throw;
        }

person Rolando Retana    schedule 16.09.2015    source источник
comment
Где вы нашли, что фильтры исключений кавычек предпочтительнее...?   -  person mason    schedule 17.09.2015
comment
В вопросе сказали, что я продублировал. (stackoverflow.com/questions/27082069/)   -  person Rolando Retana    schedule 17.09.2015
comment
Томас Левеск хорошо описал здесь . Вы это читали? Он делает несколько хороших замечаний, потому что я использовал стек и трассировку стека в своей голове взаимозаменяемо, и его пост прояснил это для меня.   -  person mason    schedule 17.09.2015
comment
Да, очень подробно, спасибо! Я думаю, что это ответ на мой вопрос.   -  person Rolando Retana    schedule 17.09.2015
comment
Вы можете спросить его, можете ли вы воспроизвести его ответ здесь, или предложить ему сделать это.   -  person mason    schedule 17.09.2015


Ответы (3)


Я не понимаю ответа Пауло. Он может быть прав, а может и нет.

Я определенно не согласен с ответом Александра. Это не просто синтаксический сахар. Чистый синтаксический сахар означает, что это просто более простой способ написать что-то, и это исполнение не изменится.

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

Обратите внимание, что я не говорю о трассировке стека (это другое, но связанное со стеком понятие). Трассировка стека останется неизменной, если вы явно не повторите исключение, как в throw exception; в блоке перехвата, где exception — это пойманное исключение.

Поэтому, хотя в некоторых случаях вы можете думать об этом как о чем-то, что может сделать ваш код чище или нет (в зависимости от вашего мнения о синтаксисе), оно действительно меняет поведение.

person mason    schedule 18.09.2015

Фильтры исключений были добавлены в C#, потому что они были в Visual Basic и команда "Roslyn" сочла их полезными при разработке "Roslyn".

Помните, что фильтр работает в контексте throw, а не в контексте catch.

Во всяком случае, одно использование может быть примерно таким:

try
{
    //...
}
catch (SqlException ex) when (ex.Number == 2)
{
    // ...
}
catch (SqlException ex)
{
    // ...
}

Отредактировано:

Можно подумать, что это просто синтаксический сахар:

try
{
    //...
}
catch (SqlException ex) when (ex.Number == 2)
{
    // ...
}
catch (SqlException ex)
{
   if (ex.Number == 2)
   {
       // ...
   }
   else
   {
       // ...
   }
}

Но если мы изменим код для этого:

try
{
    //...
}
catch (SqlException ex) when (ex.Number == 2)
{
    // ...
}

Это будет скорее так:

try
{
    //...
}
catch (SqlException ex) when (ex.Number == 2)
{
    // ...
}
catch (SqlException ex)
{
   if (ex.Number == 2)
   {
       // ...
   }
   else
   {
       throw
   }
}

Но есть одно принципиальное отличие. Исключение не перехватывается и не создается повторно, если ex.Number не равно 2. Он просто не пойман, если ex.Number не 2.

person Paulo Morgado    schedule 17.09.2015
comment
Что вы имеете в виду под бегом в контексте броска? Мне это кажется не очень ясным. - person mason; 17.09.2015
comment
Например, код в броске может выполняться под олицетворенным пользователем или иметь другие разрешения CAS. - person Paulo Morgado; 17.09.2015

UPD: Как указано в ответе Пауло Моргадо, эта функция существует в CLR уже довольно давно, а в C# 6.0 для нее добавлена ​​только синтаксическая поддержка. Однако мое понимание этого остается синтаксическим сахаром, например. синтаксис, который позволяет мне фильтровать исключения более удобным способом, чем это было раньше, независимо от того, как работает предыдущий «прямой» метод под капотом.

=====

В моем понимании, это синтаксический сахар, который позволяет вам более четко определить блок, в котором будет обрабатываться ваше исключение.

Рассмотрим следующий код:

try
{
    try
    {
        throw new ArgumentException() { Source = "One" };
        throw new ArgumentException() { Source = "Two" };
        throw new ArgumentException() { Source = "Three" };
    }
    catch (ArgumentException ex) when (ex.Source.StartsWith("One")) // local
    {
        Console.WriteLine("This error is handled locally");
    }
    catch (ArgumentException ex) when (ex.Source.StartsWith("Two")) // separate
    {
        Console.WriteLine("This error is handled locally");
    }

}
catch (ArgumentException ex) // global all-catcher
{
    Console.WriteLine("This error is handled globally");
}

Здесь вы можете ясно видеть, что первое и второе исключение обрабатываются в соответствующих блоках, которые разделены с помощью защиты when, тогда как один глобальный блок захвата всех будет перехватывать только третье исключение. Синтаксис более понятен, чем перехват всех исключений в каждом блоке, например:

    catch (ArgumentException ex)  // local
    {
                    if  (ex.Source.StartsWith("One")) 
                    {
                        Console.WriteLine("This error is handled locally");
                    } 
                    else 
                    {
                         throw;
                    }

    }
person Alexander Galkin    schedule 17.09.2015
comment
Ваш первый абзац неверен. Это функция CLR, которая теперь поддерживается языком C#. - person Paulo Morgado; 17.09.2015
comment
@PauloMorgado А? Почему первый абзац неверен? Он ничего не сказал о том, что это функция CLR, а не функция C#. - person mason; 17.09.2015
comment
Как вы понимаете синтаксический сахар? Для меня это то, что позволяет легко делать то, что можно сделать другим способом. И вы не можете делать то, что делают фильтры исключений, каким-либо другим способом в C#. - person Paulo Morgado; 18.09.2015
comment
@PauloMorgado Спасибо, что указали на CLR! Однако пользователь спрашивал не о том, КАК реализована эта функция, а о том, КОГДА он должен ее использовать. И, согласно Википедии, сюда идеально подходит определение синтаксического сахара, потому что оно ничего не говорит о реализации, а только о синтаксисе, используемом для выражения определенного понятия. - person Alexander Galkin; 18.09.2015
comment
@AlexanderGalkin, я предлагаю вам узнать, что это за функция и как она работает, чтобы дать совет о возможных вариантах использования. Ваши утверждения и образцы кода в этом ответе фактически неверны, независимо от того, что говорит Википедия. - person Paulo Morgado; 18.09.2015