Рассмотрим ситуацию, когда у меня есть три (или более) способа выполнения вычислений, каждый из которых может завершиться ошибкой за исключением. Чтобы попытаться выполнить каждый расчет, пока мы не найдем тот, который будет успешным, я сделал следующее:
double val;
try { val = calc1(); }
catch (Calc1Exception e1)
{
try { val = calc2(); }
catch (Calc2Exception e2)
{
try { val = calc3(); }
catch (Calc3Exception e3)
{
throw new NoCalcsWorkedException();
}
}
}
Есть ли какой-нибудь общепринятый образец, который позволяет добиться этого лучше? Конечно, я мог бы обернуть каждое вычисление во вспомогательный метод, который возвращает null при ошибке, а затем просто использовать ??
оператор, но есть ли способ сделать это в более общем плане (т.е. без необходимости писать вспомогательный метод для каждого метода, который я хочу использовать. )? Я думал о написании статического метода с использованием дженериков, которые обертывают любой заданный метод в try / catch и возвращают null в случае неудачи, но я не уверен, как бы я поступил с этим. Любые идеи?
источник
String
кDate
использованию ,SimpleDateFormat.parse
и мне нужно попробовать несколько различных форматы для того, как перейти к следующему , когда один бросает исключение.Ответы:
Насколько это возможно, не используйте исключения для потока управления или исключительных обстоятельств.
Но чтобы ответить на ваш вопрос напрямую (при условии, что все типы исключений одинаковы):
источник
Calc1Exception
,Calc2Exception
иCalc3Exception
имеют общий базовый класс.continue
блок catch иbreak
после блока catch, чтобы цикл заканчивался, когда вычисление работает (спасибо Лирику за этот бит)break
Утверждение, следующееcalc();
заtry
(и вообще безcontinue
оператора), может быть немного более идиоматическим.Чтобы предложить нестандартную альтернативу, как насчет рекурсивной функции ...
ПРИМЕЧАНИЕ: я ни в коем случае не говорю, что это лучший способ выполнить работу, просто другой способ.
источник
return DoCalc(c++)
эквивалентноreturn DoCalc(c)
- пост-инкрементное значение не будет передаваться глубже. Чтобы заставить это работать (и внести больше неясности), это могло быть больше похоже наreturn DoCalc((c++,c))
.Вы можете сгладить вложение, поместив его в такой метод:
Но я подозреваю, что настоящая проблема дизайна заключается в существовании трех разных методов, которые делают по сути одно и то же (с точки зрения вызывающего), но генерируют разные, не связанные между собой исключения.
Предполагается, что три исключения не связаны между собой. Если все они имеют общий базовый класс, было бы лучше использовать цикл с одним блоком catch, как предложила Ани.
источник
Старайтесь не контролировать логику, основанную на исключениях; также обратите внимание, что исключения следует выдавать только в исключительных случаях. Вычисления в большинстве случаев не должны вызывать исключения, если они не обращаются к внешним ресурсам, не анализируют строки или что-то еще. В любом случае, в худшем случае следуйте стилю TryMethod (например, TryParse ()), чтобы инкапсулировать логику исключения и сделать ваш поток управления поддерживаемым и чистым:
Другой вариант, использующий шаблон Try и объединяющий список методов вместо вложенных, если:
и если foreach немного сложен, вы можете сделать его простым:
источник
Создайте список делегатов для ваших функций вычисления, а затем создайте цикл while для их просмотра:
Это должно дать вам общее представление о том, как это сделать (т.е. это не точное решение).
источник
Exception
рыбу. ;)Exception
.Похоже, это работа для ... МОНАД! В частности, монада Maybe. Начните с монады Maybe, как описано здесь . Затем добавьте несколько методов расширения. Я написал эти методы расширения специально для той проблемы, которую вы описали. В монадах хорошо то, что вы можете написать точные методы расширения, необходимые для вашей ситуации.
Используйте это так:
Если вы обнаружите, что часто выполняете такие вычисления, монада Maybe должна уменьшить объем шаблонного кода, который вы должны написать, одновременно увеличивая читаемость вашего кода.
источник
Maybe
, не делает это решение монадическим; он использует ноль монадических свойствMaybe
и может просто использоватьnull
. Кроме того, используется «monadicly» это было бы обратное изMaybe
. Реальное монадическое решение должно было бы использовать монаду State, которая сохраняет первое неисключительное значение в качестве своего состояния, но это было бы излишним, когда работает нормальная цепная оценка.Другой вариант подхода с использованием пробного метода. Этот допускает типизированные исключения, поскольку для каждого вычисления существует тип исключения:
источник
&&
вместо этого каждое условие.В Perl вы можете это сделать
foo() or bar()
, и это будет выполнено вbar()
случаеfoo()
неудачи. В C # мы не видим эту конструкцию «if fail, then», но есть оператор, который мы можем использовать для этой цели: оператор объединения с нулевым значением??
, который продолжается, только если первая часть равна нулю.Если вы можете изменить подпись своих вычислений и либо обернуть их исключения (как показано в предыдущих сообщениях), либо переписать их, чтобы они возвращались
null
вместо этого, ваша цепочка кода станет все более короткой и по-прежнему легкой для чтения:Я использовал следующие замены для ваших функций, что привело к значению
40.40
вval
.Я понимаю, что это решение не всегда будет применимо, но вы задали очень интересный вопрос, и я считаю, что, несмотря на то, что поток относительно старый, это шаблон, который стоит рассмотреть, когда вы можете исправить это.
источник
Учитывая, что методы расчета имеют одинаковую сигнатуру без параметров, вы можете зарегистрировать их в списке, выполнить итерацию по этому списку и выполнить методы. Возможно, вам было бы даже лучше использовать
Func<double>
значение «функция, которая возвращает результат типаdouble
».источник
Вы можете использовать Task / ContinueWith и проверить исключение. Вот хороший метод расширения, который поможет сделать его красивым:
источник
Если фактический тип сгенерированного исключения не имеет значения, вы можете просто использовать блок catch без типа:
источник
if(succeeded) { break; }
пост-ловушки.И используйте это так:
источник
Похоже, что намерением ОП было найти хороший образец для решения своей проблемы и решения текущей проблемы, с которой он боролся в тот момент.
Я видел много хороших шаблонов, которые избегают вложенных блоков try catch , размещенных в этом фиде, но не нашел решения проблемы, о которой говорилось выше. Итак, вот решение:
Как упоминалось выше, он хотел создать объект-оболочку, который возвращается
null
в случае сбоя . Я бы назвал это модулем ( безопасный модуль ).Но что, если вы хотите создать безопасный модуль для результата ссылочного типа, возвращаемого функциями / методами CalcN ().
Итак, вы могли заметить, что нет необходимости «писать вспомогательный метод для каждого метода, который вы хотите использовать» .
Эти два типа стручков (для
ValueTypeResult
s- иReferenceTypeResult
с) являются достаточно .Вот код
SafePod
. Но это не контейнер. Вместо этого он создает безопасную в отношении исключений оболочку делегата дляValueTypeResult
s иReferenceTypeResult
s.Вот как вы можете использовать оператор
??
объединения с нулевым значением в сочетании с мощью первоклассных гражданских сущностейdelegate
.источник
Вы правы, оборачивая каждое вычисление, но вы должны заключать его в соответствии с принципом «говори-не-спрашивай».
Операции просты и могут независимо изменять свое поведение. И неважно, почему они по умолчанию. В качестве доказательства вы можете реализовать calc1DefaultingToCalc2 как:
источник
Похоже, ваши расчеты содержат более достоверную информацию, чем просто сам расчет. Возможно, для них было бы разумнее выполнить свою собственную обработку исключений и вернуть класс «результатов», содержащий информацию об ошибках, информацию о значениях и т. Д. Подумайте, как класс AsyncResult следует асинхронному шаблону. Затем вы можете оценить реальный результат расчета. Вы можете рационализировать это, думая о том, что если расчет не удастся, это так же информативно, как если бы он прошел. Следовательно, исключение - это информация, а не «ошибка».
Конечно, меня больше интересует общая архитектура, которую вы используете для выполнения этих вычислений.
источник
while (!calcResult.HasValue) nextCalcResult()
списка Calc1, Calc2, Calc3 и т. Д.А как насчет отслеживания ваших действий ...
источник
track
в данном случае), и я точно знаю , какой сегмент в моем коде привел к сбою блока. Может быть, вам следовало бы уточнить, чтобы сообщить таким участникам, как DeCaf, чтоtrack
сообщение отправляется вашей настраиваемой процедуре обработки ошибок, которая позволяет вам отлаживать ваш код. Похоже, он не понял твоей логики.