Питання Спробуйте одночасно кілька винятків?


Це не рекомендується просто ловити System.Exception. Натомість треба попасти лише "відомі" винятки.

Тепер це іноді призводить до непотрібного повторюваного коду, наприклад:

try
{
    WebId = new Guid(queryString["web"]);
}
catch (FormatException)
{
    WebId = Guid.Empty;
}
catch (OverflowException)
{
    WebId = Guid.Empty;
}

Я задаюсь питанням: чи є спосіб зловити обидві винятки і тільки зателефонувати WebId = Guid.Empty дзвонити один раз?

Даний приклад досить простий, тому що це лише а GUID. Але уявіть собі код, у якому ви модифікуєте об'єкт кілька разів, і якщо одна з маніпуляцій збігається очікуваним чином, ви хочете "скинути" object. Проте, якщо є несподіване виняток, я все ж хочу кинути це вище.


1712
2017-09-25 20:56


походження


Якщо ви використовуєте .net 4 та вище, я вважаю за краще використовувати aggregateexception msdn.microsoft.com/en-us/library/system.aggregateexception.aspx - Bepenfriends
Bepenfriends- З того часу System.Guid не кидає AggregateException, було б здорово, якщо б ти (або хтось) міг опублікувати відповідь, показуючи, як ви обернете його в AggregateException тощо. - weir
Про використання AggregateException: Вставити в AggregateException свій власний код - DavidRR
"Не рекомендується просто зловити System.Exception". - А якщо метод може викинути 32 типи виключень, що робить? написати зловити для кожного з них окремо? - giorgi.m
Тримайте це так, як у вас є. Перемістіть код у обробника помилок, якщо ви хочете, так що існує лише одна рядова лінія в одному випадку. - rolls


Відповіді:


Виловити System.Exception і увімкніть типи

catch (Exception ex)            
{                
    if (ex is FormatException || ex is OverflowException)
    {
        WebId = Guid.Empty;
        return;
    }

    throw;
}

1792
2017-09-25 21:01



На жаль, FxCop (тобто - Visual Studio Code Analysis) не подобається, коли ви сповіщаєте Виняток. - Andrew Garrison
Я погоджуюсь з тим, що не допускаю винятку, але в цьому випадку зловмисне це фільтр. Ви можете мати шар вище, який буде обробляти інші типи виключень. Я б сказав, що це правильно, навіть якщо він включає улов (виняток x). Він не змінює потік програми, він просто обробляє певні винятки, а інший додаток додає інші типи виключень. - lkg
Остання версія FxCop не викидає виключення, коли використовується код вище. - Peter
Не впевнений, що було помилково з кодом OP, в першу чергу. # 1 прийнята відповідь майже вдвічі більше рядків і набагато менш читабельною. - João Bragança
@ JoãoBragança: Хоча ця відповідь у цьому прикладі використовує більше рядків, спробуйте уявити, наприклад, якщо ви маєте справу з файлом IO, і все, що ви хочете зробити, це зловживання цими винятками та виконання деяких журнальних повідомлень, але лише ті, які ви очікуєте від вашої файли IO методів. Тоді вам часто доводиться мати справу з більшим числом (близько 5 і більше) різних видів винятків. У такому випадку такий підхід може заощадити кілька рядків. - Xilconic


EDIT: Я згоден з тими, хто каже, що, як і в C # 6.0, фільтри винятків тепер чудово підходять. catch (Exception ex) when (ex is ... || ex is ... )

Крім того, що я все ще ненавиджу маскування з однією довгою лінією і особисто викладе код, як показано нижче. Я думаю, що це настільки ж функціональне, як і естетичне, оскільки я вважаю, що це покращує розуміння. Деякі з них можуть не погодитися:

catch (Exception ex) when (
    ex is ...
    || ex is ...
    || ex is ...
)

ОРИГІНАЛ:

Я знаю, що я тут трохи пізно, але святий дим ...

Різка прямо до погоні, такий тип дублює попередню відповідь, але якщо ви дійсно хочете виконати спільну дію для декількох типів виключень і тримати все це акуратно і охайно в рамках одного методу, то чому б не просто використовувати лямбда / closing / inline функція робити щось на зразок наступного? Я маю на увазі, шанси дуже добре, що ви в кінцевому підсумку зрозумієте, що ви просто хочете зробити це закриття окремим методом, який ви можете використовувати всюди. Але тоді це буде надто легко зробити, не будучи фактично змінювати решту коду структурно. Правильно?

private void TestMethod ()
{
    Action<Exception> errorHandler = ( ex ) => {
        // write to a log, whatever...
    };

    try
    {
        // try some stuff
    }
    catch ( FormatException  ex ) { errorHandler ( ex ); }
    catch ( OverflowException ex ) { errorHandler ( ex ); }
    catch ( ArgumentNullException ex ) { errorHandler ( ex ); }
}

Я не можу не дивуватися (УВАГА: трохи іронія / сарказм вперед) чому на землі йдуть всі ці зусилля, щоб в принципі просто замінити наступне:

try
{
    // try some stuff
}
catch( FormatException ex ){}
catch( OverflowException ex ){}
catch( ArgumentNullException ex ){}

... з якимось божевільним варіантом цього наступного запаху коду, я маю на увазі приклад, лише прикидаюсь, що ви рятуєте кілька натискань клавіш.

// sorta sucks, let's be honest...
try
{
    // try some stuff
}
catch( Exception ex )
{
    if (ex is FormatException ||
        ex is OverflowException ||
        ex is ArgumentNullException)
    {
        // write to a log, whatever...
        return;
    }
    throw;
}

Тому що воно, звичайно, не є автоматично читабельним.

Звичайно, я залишив три ідентичні випадки /* write to a log, whatever... */ return; з першого прикладу.

Але це моя точка зору. Ви чули про функції / методи, чи не так? Серйозно Напишіть загальне ErrorHandler функція і, як виклик, з кожного блокування.

Якщо ви запитаєте мене, другий приклад (з if і is ключові слова) є значно менш читабельним, і одночасно значно більше піддається помилці під час фази підтримки вашого проекту.

Фаза технічного обслуговування, для тих, хто може бути відносно новим для програмування, складатиме 98,7% або більше загальної тривалості вашого проекту, а бідний шаблон, що виконує технічне обслуговування, майже напевно стане хтось інший, ніж ви. І є дуже хороший шанс, що вони витратять 50% свого часу на роботу, проклинаючи своє ім'я.

І, звичайно, FxCop торкається до вас, і ви повинні такождодайте атрибут до свого коду, який має точно посмішку, що стосується запущеної програми, і лише там, щоб повідомити FxCop проігнорувати проблему, яка в 99,9% випадків цілком правильна під час позначення. І, вибачте, я можу помилятися, але чи не "агнорувати" цей атрибут, фактично складені у вашому додатку?

Поставив би весь це if Тест на одній лінії робить його більш читабельним? Я так не думаю. Я маю на увазі, у мене був інший програміст, який якось давно стверджував, що розміщення більшого коду на одному рядку змусить його "працювати швидше". Але, звичайно, він був різко гострий. Спроба пояснити йому (з прямим обличчям, що було складним завданням), як інтерпретатор або компілятор порушив б цю довгу лінію окремо в дискретних твердженнях однієї інструкції на рядок - по суті ідентичний результату, якщо б він пішов вперед і просто зробив код читабельним, а не намагався розумно компілятора - нічого не вплинуло на нього. Але я відволікся.

Скільки менше чи читається це, коли ви додаєте ще три типи виключень, через місяць або два? (Відповідь: він отримує a багато менш читабельна)

Одне з головних моментів, по суті, полягає в тому, що більшість з точки зору форматування текстового вихідного коду, який ми всі дивимося на кожен день, полягає в тому, щоб зробити це справді дійсно очевидним для інших людей, що насправді відбувається при запуску коду. Оскільки компілятор перетворює вихідний код на щось зовсім інше і не може більше турбуватися про ваш стиль форматування коду. Так все-на-один-лінія повністю всмоктується теж.

Просто кажу...

// super sucks...
catch( Exception ex )
{
    if ( ex is FormatException || ex is OverflowException || ex is ArgumentNullException )
    {
        // write to a log, whatever...
        return;
    }
    throw;
}

370
2017-10-12 00:24



Коли я спочатку спіткнувся на це питання, я був у всьому прийнятої відповіді. Прохолодний я можу просто зловити все Exceptions і перевірте тип. Я думав, що очистив цей код, але щось мене примусило повернутися до питання, і я фактично читав інші відповіді на питання. Я жував на деякий час, але я повинен згодна з вами. це є більше зручний для читання та обслуговуючий, щоб використовувати функцію для вичерпання коду, ніж зловити все, перевірте порівняння типу зі списком, кодом обгортки та кидком. Дякуємо, що запізнилися і запропонували альтернативний та раціональний (IMO) варіант. +1 - erroric
Використання функції обробки помилок не спрацює, якщо ви хочете включити a throw;. Вам доведеться повторити цю лінію коду в кожному блоці зловити (очевидно, не кінець світу, але варто згадати, оскільки це код, який потрібно буде повторити). - kad81
@ ka81, це правда, але ви все одно отримаєте перевагу написання коду реєстрації та очищення в одному місці, а також, якщо потрібно, змінити його в одному місці, без суворої семантики лову базового типу винятку, то розгалуження на основі виняток типу. І це ще одне throw(); Заява в кожному блоці лову - це невелика ціна, яку ви платите, IMO, і, як і раніше, залишає вас у змозі зробити додаткову очистку від виключення, якщо це необхідно. - Craig
Привіт @ Reitffunk, просто використовуйте Func<Exception, MyEnumType> замість Action<Exception>. Це так Func<T, Result>, с Result будучи типом повернення - Craig
Я в повній згоді тут. Я теж читаю першу відповідь, і думка виглядає логічним. Переміщено до загальної 1 для всіх обробників виключень. Щось усередині мене змушувало мене внутрішньо ... тому я повернув код. Тоді натрапила ця краса! Це потреби бути прийнятою відповіддю - Conor Gallagher


Як інші вказали, ви можете мати if Заява всередині вашого блоку виявлення, щоб визначити, що відбувається. C # 6 підтримує фільтри виключення, тому працюватиме наступне:

try { … }
catch (Exception e) when (MyFilter(e))
{
    …
}

The MyFilter спосіб може виглядати приблизно так:

private bool MyFilter(Exception e)
{
  return e is ArgumentNullException || e is FormatException;
}

Крім того, це все можна зробити вбудованим (праворуч, коли оператор повинен бути логічним виразом).

try { … }
catch (Exception e) when (e is ArgumentNullException || e is FormatException)
{
    …
}

Це відрізняється від використання if заява зсередини catch блокувати, використовуючи фільтри виключення не буде розмотати стек.

Ви можете завантажити Visual Studio 2015 перевірити це.

Якщо ви хочете продовжувати використовувати Visual Studio 2013, ви можете встановити наступний пакет nuget:

Установка-пакет Microsoft.Net.Compilers

На момент написання, це буде включати підтримку для C # 6.

Посилання на цей пакет призведе до побудови проекту за допомогою   конкретна версія компіляторів C # і Visual Basic, що містяться в   пакет, на відміну від будь-якої встановленої версії системи.


241
2018-04-04 13:59



Терпеливо чекаючи офіційного випуску 6 ... Я хотів би побачити це отримати чек, коли це станеться. - RubberDuck
@RubberDuck Я вмираю за нульовий оператор розповсюдження з C # 6. Намагаюся переконати решту моєї команди, що це коштує небезпеки нестабільної мови / компілятора. Багато незначних покращень з величезним впливом. Що стосується одержання позначеної як відповіді, то не важливо, поки люди усвідомлюють, що це буде / можливо, я щаслива. - Joe
Правильно ?! Я буду добре розглядати мою кодову базу в найближчому майбутньому. =) Я знаю, що перевірка не є важливою, але, якщо прийнята відповідь буде незабаром застарілою, я сподіваюсь, що О.П. повернеться, щоб перевірити це, щоб надати йому відповідну видимість. - RubberDuck
Це частково чому я ще не нагороджую його ще @ Joe. Я хочу, щоб це було видимим. Можливо, ви хочете додати приклад вбудованого фільтра для чіткості. - RubberDuck


Не в C #, на жаль, тому що вам потрібен фільтр винятків, щоб зробити це, і C # не виставляє цю особливість MSIL. VB.NET має цю можливість, хоча, наприклад,

Catch ex As Exception When TypeOf ex Is FormatException OrElse TypeOf ex Is OverflowException

Що ви можете зробити, це використовувати анонімну функцію для інкапсуляції вашого коду помилки під час помилки, а потім називайте її цими конкретними блоками вилучення:

Action onError = () => WebId = Guid.Empty;
try
{
    // something
}
catch (FormatException)
{
    onError();
}
catch (OverflowException)
{
    onError();
}

184
2017-09-25 21:03



Цікава ідея та ще один приклад, що іноді VB.net має деякі цікаві переваги над C # - Michael Stum♦
@MichaelStum з це різновид синтаксису я навряд чи називаю це цікавим зовсім ... здригати - MarioDS
Фільтри винятку надходять в c # 6! Зверніть увагу на різницю використання фільтрів на користь відновлення roslyn.codeplex.com/discussions/541301 - Arne Deruwe
@ArneDeruwe Дякую за цю посилання! Я тільки що дізнався ще одну важливу причину не повторно кинути: throw e; руйнує stacktrace і callstack throw; знищує "тільки" callstack (надання аварійно-відвалу даремно!) A дуже Причину не можна використовувати, якщо його можна уникнути! - AnorZaken
Якщо я не помиляюсь, ви також можете прорватися через улов в VB, як ви можете з випадку або (переключитися в C #) заяви, як це Try Catch ex As ArgumentException Catch ex As NullReferenceException End Try Але, на жаль, C # не так, щоб ми залишили допоміжний метод, або щоб загально знати і визначити тип. - David Carrigan


Заради завершеності, оскільки .NET 4.0код може бути переписаний як:

Guid.TryParse(queryString["web"], out WebId);

TryParse ніколи не викидає винятки і повертає помилково, якщо формат невірний, налаштування WebId на Guid.Empty.


З тих пір C # 7 ви можете уникнути введення змінної на окрему рядок:

Guid.TryParse(queryString["web"], out Guid webId);

Ви також можете створювати методи для розбору повернених кортежів, які ще не доступні в .NET Framework версії 4.6.

(bool success, Guid result) TryParseGuid(string input) =>
    (Guid.TryParse(input, out Guid result), result);

І використовуйте їх так:

WebId = TryParseGuid(queryString["web"]).result;
// or
var tuple = TryParseGuid(queryString["web"]);
WebId = tuple.success ? tuple.result : DefaultWebId;

Наступне непотрібне оновлення цієї непотрібної відповіді виникає, коли в C # 12 відбувається деконструкція out-параметрів. :)


123
2018-04-13 12:18



Точно - лаконічно, і ви повністю порушуєте штраф за виконання винятком, погана форма навмисного використання винятків для керування потоком програм і м'який фокус на те, щоб ваша логіка перетворення була розповсюджена, трохи тут і дещо там . - Craig
Я знаю, що ви мали на увазі, але звичайно Guid.TryParse ніколи не повертається Guid.Empty. Якщо рядок у неправильному форматі, він встановлює result вихідний параметр до Guid.Empty, Але це повертає  false. Я згадую це, тому що я бачив код, який робить речі в стилі Guid.TryParse(s, out guid); if (guid == Guid.Empty) { /* handle invalid s */ }, який зазвичай неправильний, якщо s може бути рядковим представленням Guid.Empty. - hvd
як ти відповідаєш на питання, хіба що це не в дусі питання. Більша проблема - це щось інше :( - nawfal
Правильний шаблон для використання TryParse, звичайно, більше схожий if( Guid.TryParse(s, out guid){ /* success! */ } else { /* handle invalid s */ }, що не залишає ніякої двозначності, наприклад, зламаного прикладу, де вхідне значення може бути дійсним представленням рядка Guid. - Craig
Ця відповідь дійсно може бути правильною щодо Guid.Parse, але вона пропустила всю точку початкового питання. Котрий не мав нічого спільного з Guid.Parse, але мав відношення до лову винятків vs FormatException / OverflowException / etc. - Conor Gallagher


Якщо ви можете оновити свою заявку на C # 6, вам пощастить. У новій версії C # реалізовано фільтри виключення. Отже, ви можете написати це:

catch (Exception ex) when (ex is FormatException || ex is OverflowException) {
    WebId = Guid.Empty;
}

Деякі люди вважають цей код таким же, як і

catch (Exception ex) {                
    if (ex is FormatException || ex is OverflowException) {
        WebId = Guid.Empty;
    }
    throw;
}

Але це не так. Насправді це єдина нова функція в C # 6, яку неможливо емулювати у попередніх версіях. По-перше, перекидання означає більше накладних витрат, ніж пропуск піраміда. По-друге, це не є семантично еквівалентним. Нова функція зберігає стек недоступним під час налагодження коду. Без цієї можливості аварійне скидання є менш корисним або навіть марним.

Див обговорення цього питання на CodePlex. І а приклад показує різницю.


62
2018-04-01 12:29



Кинути без винятку зберігає стек, але "throw ex" перезапише його. - Ivan


Якщо ви не хочете використовувати if заява в межах catch Області застосування в C# 6.0 ви можете використовувати Exception Filters синтаксис який вже підтримується CLR в попередніх версіях, але існував лише в VB.NET/MSIL:

try
{
    WebId = new Guid(queryString["web"]);
}
catch (Exception exception) when (exception is FormatException || ex is OverflowException)
{
    WebId = Guid.Empty;
}

Цей код буде ловити Exception тільки коли це InvalidDataException або ArgumentNullException.

Насправді, ви можете поставити в основному будь-який стан усередині цього when пункт:

static int a = 8;

...

catch (Exception exception) when (exception is InvalidDataException && a == 8)
{
    Console.WriteLine("Catch");
}

Зауважте, що на відміну від if заява всередині catchРосійська сфера Exception Filters не може кинути Exceptions, і коли вони роблять, або коли стан не є true, наступний catch стан буде оцінено замість:

static int a = 7;

static int b = 0;

...

try
{
    throw new InvalidDataException();
}
catch (Exception exception) when (exception is InvalidDataException && a / b == 2)
{
    Console.WriteLine("Catch");
}
catch (Exception exception) when (exception is InvalidDataException || exception is ArgumentException)
{
    Console.WriteLine("General catch");
}

Вихід: загальний вилов.

Коли є більше одного true  Exception Filter - перший прийметься:

static int a = 8;

static int b = 4;

...

try
{
    throw new InvalidDataException();
}
catch (Exception exception) when (exception is InvalidDataException && a / b == 2)
{
    Console.WriteLine("Catch");
}
catch (Exception exception) when (exception is InvalidDataException || exception is ArgumentException)
{
    Console.WriteLine("General catch");
}

Вихід: Спіймати.

І як ви можете бачити в MSIL код не перекладений if заяви, але до Filters, і Exceptions не можна кидати з-поміж районів, позначених Filter 1 і Filter 2 але фільтр кидає Exception буде невдало, а також останнє значення порівняння, натиснуте на стек до endfilter команда визначить успіх / невдачу фільтра (Catch 1  XOR  Catch 2 виконає відповідно):

Exception Filters MSIL

Також, конкретно Guid має Guid.TryParse метод


26
2017-10-07 17:31