Шаблон, чтобы избежать вложенных блоков try catch?

114

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

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 в случае неудачи, но я не уверен, как бы я поступил с этим. Любые идеи?

jjoelson
источник
Не могли бы вы рассказать подробнее о расчетах?
Джеймс Джонсон
2
По сути, это просто разные методы решения / аппроксимации PDE. Они из сторонней библиотеки, поэтому я не могу изменить их, чтобы они возвращали коды ошибок или null. Лучшее, что я мог сделать, - это обернуть каждый метод индивидуально.
jjoelson
Являются ли методы calc частью вашего проекта (а не сторонней библиотекой)? Если это так, вы можете извлечь логику, которая генерирует исключения, и использовать ее, чтобы решить, какой метод calc необходимо вызвать.
Крис
1
Существует еще один вариант использования для этого , что я наткнулся на Java - Мне нужно , чтобы разобрать Stringк Dateиспользованию , SimpleDateFormat.parseи мне нужно попробовать несколько различных форматы для того, как перейти к следующему , когда один бросает исключение.
Miserable Variable

Ответы:

125

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

Но чтобы ответить на ваш вопрос напрямую (при условии, что все типы исключений одинаковы):

Func<double>[] calcs = { calc1, calc2, calc3 };

foreach(var calc in calcs)
{
   try { return calc(); }
   catch (CalcException){  }
} 

throw new NoCalcsWorkedException();
Ани
источник
15
Это предполагает, что Calc1Exception, Calc2Exceptionи Calc3Exceptionимеют общий базовый класс.
Wyzard
3
Сверху он предполагает общую подпись - что на самом деле не так уж и далеко. Хороший ответ.
TomTom
1
Кроме того, я добавил continueблок catch и breakпосле блока catch, чтобы цикл заканчивался, когда вычисление работает (спасибо Лирику за этот бит)
jjoelson
6
+1 только потому, что в нем написано «Не используйте исключения для потока управления», хотя я бы использовал «НИКОГДА НИКОГДА» вместо «Насколько это возможно».
Bill K
1
@jjoelson: breakУтверждение, следующее calc();за try(и вообще без continueоператора), может быть немного более идиоматическим.
Адам Робинсон
38

Чтобы предложить нестандартную альтернативу, как насчет рекурсивной функции ...

//Calling Code
double result = DoCalc();

double DoCalc(int c = 1)
{
   try{
      switch(c){
         case 1: return Calc1();
         case 2: return Calc2();
         case 3: return Calc3();
         default: return CalcDefault();  //default should not be one of the Calcs - infinite loop
      }
   }
   catch{
      return DoCalc(++c);
   }
}

ПРИМЕЧАНИЕ: я ни в коем случае не говорю, что это лучший способ выполнить работу, просто другой способ.

musefan
источник
6
Однажды мне пришлось реализовать «Возобновить следующее при ошибке» на языке, и код, который я сгенерировал, выглядел примерно так.
Джейкоб Кролл
4
Пожалуйста, никогда не используйте оператор switch для создания цикла for.
Джефф Ферланд
3
Оператор switch для зацикливания не обслуживается
Мохамед Абед
1
Я знаю, что мой ответ - не самый эффективный код, но опять же, использование блоков try / catch для такого рода вещей в любом случае не лучший способ. К сожалению, OP использует стороннюю библиотеку и должен сделать все возможное, чтобы добиться успеха. В идеале входные данные можно было бы сначала проверить и выбрать правильную функцию расчета, чтобы гарантировать, что он не потерпит неудачу - конечно, вы можете затем поместить все это в try / catch на всякий случай;)
Musefan
1
return DoCalc(c++)эквивалентно return DoCalc(c)- пост-инкрементное значение не будет передаваться глубже. Чтобы заставить это работать (и внести больше неясности), это могло быть больше похоже на return DoCalc((c++,c)).
Artur Czajka
37

Вы можете сгладить вложение, поместив его в такой метод:

private double calcStuff()
{
  try { return calc1(); }
  catch (Calc1Exception e1)
  {
    // Continue on to the code below
  }

  try { return calc2(); }
  catch (Calc2Exception e1)
  {
    // Continue on to the code below
  }

  try { return calc3(); }
  catch (Calc3Exception e1)
  {
    // Continue on to the code below
  }

  throw new NoCalcsWorkedException();
}

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

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

Wyzard
источник
1
+1: Это самое чистое и серьезное решение проблемы. Другие решения, которые я вижу здесь, просто пытаются быть милыми, ИМО. Как сказал OP, он не писал API, поэтому он застрял с генерируемыми исключениями.
Nate CK
19

Старайтесь не контролировать логику, основанную на исключениях; также обратите внимание, что исключения следует выдавать только в исключительных случаях. Вычисления в большинстве случаев не должны вызывать исключения, если они не обращаются к внешним ресурсам, не анализируют строки или что-то еще. В любом случае, в худшем случае следуйте стилю TryMethod (например, TryParse ()), чтобы инкапсулировать логику исключения и сделать ваш поток управления поддерживаемым и чистым:

bool TryCalculate(out double paramOut)
{
  try
  {
    // do some calculations
    return true;
  }
  catch(Exception e)
  { 
     // do some handling
    return false;
  }

}

double calcOutput;
if(!TryCalc1(inputParam, out calcOutput))
  TryCalc2(inputParam, out calcOutput);

Другой вариант, использующий шаблон Try и объединяющий список методов вместо вложенных, если:

internal delegate bool TryCalculation(out double output);

TryCalculation[] tryCalcs = { calc1, calc2, calc3 };

double calcOutput;
foreach (var tryCalc in tryCalcs.Where(tryCalc => tryCalc(out calcOutput)))
  break;

и если foreach немного сложен, вы можете сделать его простым:

        foreach (var tryCalc in tryCalcs)
        {
            if (tryCalc(out calcOutput)) break;
        }
Мохамед Абед
источник
Честно говоря, я думаю, что это просто вызывает ненужные абстракции. Это не ужасное решение, но я бы не стал его использовать в большинстве случаев.
user606723
Если вам не важен тип исключения, и вы просто хотите обрабатывать условный код ... таким образом, определенно лучше с точки зрения абстракции и ремонтопригодности преобразовать его в условный метод с возвратом независимо от того, успешно он или нет, таким образом вы скрываете беспорядочный синтаксис обработки исключений с помощью ясного описательного метода ... тогда ваш код будет обрабатывать его, как обычный условный метод.
Мохамед Абед
Я знаю пункты, и они верны. Однако при использовании этого типа абстракции (сокрытие беспорядка / сложности) почти везде становится нелепо, а понимание того, что это такое, становится намного сложнее. Как я уже сказал, это неплохое решение, но я бы не стал использовать его легкомысленно.
user606723
9

Создайте список делегатов для ваших функций вычисления, а затем создайте цикл while для их просмотра:

List<Func<double>> calcMethods = new List<Func<double>>();

// Note: I haven't done this in a while, so I'm not sure if
// this is the correct syntax for Func delegates, but it should
// give you an idea of how to do this.
calcMethods.Add(new Func<double>(calc1));
calcMethods.Add(new Func<double>(calc2));
calcMethods.Add(new Func<double>(calc3));

double val;
for(CalcMethod calc in calcMethods)
{
    try
    {
        val = calc();
        // If you didn't catch an exception, then break out of the loop
        break;
    }
    catch(GenericCalcException e)
    {
        // Not sure what your exception would be, but catch it and continue
    }

}

return val; // are you returning the value?

Это должно дать вам общее представление о том, как это сделать (т.е. это не точное решение).

Кирил
источник
1
За исключением, конечно, того факта, что обычно вы никогда не должны ловить Exceptionрыбу. ;)
DeCaf
@DeCaf, как я уже сказал: это «должно дать вам общее представление о том, как это сделать (т.е. не точное решение)». Таким образом, OP может поймать любое подходящее исключение ... нет необходимости ловить общий Exception.
Кирилл
Да, извини, я просто почувствовал необходимость рассказать об этом.
DeCaf
1
@DeCaf, это верное разъяснение для тех, кто не знаком с лучшими практиками. Спасибо :)
Кирилл
9

Похоже, это работа для ... МОНАД! В частности, монада Maybe. Начните с монады Maybe, как описано здесь . Затем добавьте несколько методов расширения. Я написал эти методы расширения специально для той проблемы, которую вы описали. В монадах хорошо то, что вы можете написать точные методы расширения, необходимые для вашей ситуации.

public static Maybe<T> TryGet<T>(this Maybe<T> m, Func<T> getFunction)
{
    // If m has a value, just return m - we want to return the value
    // of the *first* successful TryGet.
    if (m.HasValue)
    {
        return m;
    }

    try
    {
        var value = getFunction();

        // We were able to successfully get a value. Wrap it in a Maybe
        // so that we can continue to chain.
        return value.ToMaybe();
    }
    catch
    {
        // We were unable to get a value. There's nothing else we can do.
        // Hopefully, another TryGet or ThrowIfNone will handle the None.
        return Maybe<T>.None;
    }
}

public static Maybe<T> ThrowIfNone<T>(
    this Maybe<T> m,
    Func<Exception> throwFunction)
{
    if (!m.HasValue)
    {
        // If m does not have a value by now, give up and throw.
        throw throwFunction();
    }

    // Otherwise, pass it on - someone else should unwrap the Maybe and
    // use its value.
    return m;
}

Используйте это так:

[Test]
public void ThrowIfNone_ThrowsTheSpecifiedException_GivenNoSuccessfulTryGet()
{
    Assert.That(() =>
        Maybe<double>.None
            .TryGet(() => { throw new Exception(); })
            .TryGet(() => { throw new Exception(); })
            .TryGet(() => { throw new Exception(); })
            .ThrowIfNone(() => new NoCalcsWorkedException())
            .Value,
        Throws.TypeOf<NoCalcsWorkedException>());
}

[Test]
public void Value_ReturnsTheValueOfTheFirstSuccessfulTryGet()
{
    Assert.That(
        Maybe<double>.None
            .TryGet(() => { throw new Exception(); })
            .TryGet(() => 0)
            .TryGet(() => 1)
            .ThrowIfNone(() => new NoCalcsWorkedException())
            .Value,
        Is.EqualTo(0));
}

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

fre0n
источник
2
Мне нравится это решение. Однако он довольно непрозрачен для тех, кто ранее не сталкивался с монадами, а это значит, что это очень далеко от идиоматики в C #. Я бы не хотел, чтобы один из моих коллег изучал монады только для того, чтобы модифицировать этот глупый кусок кода в будущем. Тем не менее, это отлично подходит для использования в будущем.
jjoelson
1
+1 за чувство юмора, за то, что написал наиболее тупое и подробное решение этой проблемы, а затем сказал, что это «уменьшит количество шаблонного кода, который вы должны написать, одновременно увеличивая читаемость вашего кода».
Nate CK
1
Эй, мы не жалуемся на огромное количество кода, скрывающегося в System.Linq, и с радостью используем эти монады весь день. Я думаю, что @ fre0n просто означает, что если вы хотите включить монаду Maybe в свой инструментарий, на такие связанные оценки станет легче смотреть и рассуждать. Есть несколько реализаций, которые легко схватить.
Себастьян Гуд
Просто потому, что он использует Maybe, не делает это решение монадическим; он использует ноль монадических свойств Maybeи может просто использовать null. Кроме того, используется «monadicly» это было бы обратное из Maybe. Реальное монадическое решение должно было бы использовать монаду State, которая сохраняет первое неисключительное значение в качестве своего состояния, но это было бы излишним, когда работает нормальная цепная оценка.
Dax Fohl
7

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

    public bool Try<T>(Func<double> func, out double d) where T : Exception
    {
      try
      {
        d = func();
        return true;
      }
      catch (T)
      {
        d = 0;
        return false;
      }
    }

    // usage:
    double d;
    if (!Try<Calc1Exception>(() = calc1(), out d) && 
        !Try<Calc2Exception>(() = calc2(), out d) && 
        !Try<Calc3Exception>(() = calc3(), out d))

      throw new NoCalcsWorkedException();
    }
Стефан
источник
Фактически вы можете избежать вложенных if, используя &&вместо этого каждое условие.
DeCaf
4

В Perl вы можете это сделать foo() or bar(), и это будет выполнено в bar()случае foo()неудачи. В C # мы не видим эту конструкцию «if fail, then», но есть оператор, который мы можем использовать для этой цели: оператор объединения с нулевым значением ??, который продолжается, только если первая часть равна нулю.

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

double? val = Calc1() ?? Calc2() ?? Calc3() ?? Calc4();
if(!val.HasValue) 
    throw new NoCalcsWorkedException();

Я использовал следующие замены для ваших функций, что привело к значению 40.40в val.

static double? Calc1() { return null; /* failed */}
static double? Calc2() { return null; /* failed */}
static double? Calc3() { return null; /* failed */}
static double? Calc4() { return 40.40; /* success! */}

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

Абель
источник
1
Я просто хочу сказать «Спасибо». Я пытался реализовать то, о чем вы говорили . Надеюсь, я правильно понял.
AlexMelw
3

Учитывая, что методы расчета имеют одинаковую сигнатуру без параметров, вы можете зарегистрировать их в списке, выполнить итерацию по этому списку и выполнить методы. Возможно, вам было бы даже лучше использовать Func<double>значение «функция, которая возвращает результат типа double».

using System;
using System.Collections.Generic;

namespace ConsoleApplication1
{
  class CalculationException : Exception { }
  class Program
  {
    static double Calc1() { throw new CalculationException(); }
    static double Calc2() { throw new CalculationException(); }
    static double Calc3() { return 42.0; }

    static void Main(string[] args)
    {
      var methods = new List<Func<double>> {
        new Func<double>(Calc1),
        new Func<double>(Calc2),
        new Func<double>(Calc3)
    };

    double? result = null;
    foreach (var method in methods)
    {
      try {
        result = method();
        break;
      }
      catch (CalculationException ex) {
        // handle exception
      }
     }
     Console.WriteLine(result.Value);
   }
}
Марчин Серединский
источник
3

Вы можете использовать Task / ContinueWith и проверить исключение. Вот хороший метод расширения, который поможет сделать его красивым:

    static void Main() {
        var task = Task<double>.Factory.StartNew(Calc1)
            .OrIfException(Calc2)
            .OrIfException(Calc3)
            .OrIfException(Calc4);
        Console.WriteLine(task.Result); // shows "3" (the first one that passed)
    }

    static double Calc1() {
        throw new InvalidOperationException();
    }

    static double Calc2() {
        throw new InvalidOperationException();
    }

    static double Calc3() {
        return 3;
    }

    static double Calc4() {
        return 4;
    }
}

static class A {
    public static Task<T> OrIfException<T>(this Task<T> task, Func<T> nextOption) {
        return task.ContinueWith(t => t.Exception == null ? t.Result : nextOption(), TaskContinuationOptions.ExecuteSynchronously);
    }
}
Дакс Фол
источник
1

Если фактический тип сгенерированного исключения не имеет значения, вы можете просто использовать блок catch без типа:

var setters = new[] { calc1, calc2, calc3 };
bool succeeded = false;
foreach(var s in setters)
{
    try
    {
            val = s();
            succeeded = true;
            break;
    }
    catch { /* continue */ }
}
if (!suceeded) throw new NoCalcsWorkedException();
Джейкоб Кролл
источник
Разве это не всегда вызывает каждую функцию в списке? Может, захочется бросить (не каламбур) что-то вроде if(succeeded) { break; }пост-ловушки.
CVn
1
using System;

namespace Utility
{
    /// <summary>
    /// A helper class for try-catch-related functionality
    /// </summary>
    public static class TryHelper
    {
        /// <summary>
        /// Runs each function in sequence until one throws no exceptions;
        /// if every provided function fails, the exception thrown by
        /// the final one is left unhandled
        /// </summary>
        public static void TryUntilSuccessful( params Action[] functions )
        {
            Exception exception = null;

            foreach( Action function in functions )
            {
                try
                {
                    function();
                    return;
                }
                catch( Exception e )
                {
                    exception   = e;
                }
            }

            throw exception;
        }
    }
}

И используйте это так:

using Utility;

...

TryHelper.TryUntilSuccessful(
    () =>
    {
        /* some code */
    },
    () =>
    {
        /* more code */
    },
    calc1,
    calc2,
    calc3,
    () =>
    {
        throw NotImplementedException();
    },
    ...
);
Райан Лестер
источник
1

Похоже, что намерением ОП было найти хороший образец для решения своей проблемы и решения текущей проблемы, с которой он боролся в тот момент.

ОП: «Я мог бы заключить каждое вычисление во вспомогательный метод, который возвращает ноль в случае ошибки, а затем просто использовать ??оператор, но есть ли способ сделать это в более общем плане (т.е. без необходимости писать вспомогательный метод для каждого метода, который я хочу use)? Я думал о написании статического метода с использованием универсальных шаблонов, которые обертывают любой заданный метод в try / catch и возвращают null в случае неудачи, но я не уверен, как бы я поступил с этим. Есть идеи? "

Я видел много хороших шаблонов, которые избегают вложенных блоков try catch , размещенных в этом фиде, но не нашел решения проблемы, о которой говорилось выше. Итак, вот решение:

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

public static void Run()
{
    // The general case
    // var safePod1 = SafePod.CreateForValueTypeResult(() => CalcX(5, "abc", obj));
    // var safePod2 = SafePod.CreateForValueTypeResult(() => CalcY("abc", obj));
    // var safePod3 = SafePod.CreateForValueTypeResult(() => CalcZ());

    // If you have parameterless functions/methods, you could simplify it to:
    var safePod1 = SafePod.CreateForValueTypeResult(Calc1);
    var safePod2 = SafePod.CreateForValueTypeResult(Calc2);
    var safePod3 = SafePod.CreateForValueTypeResult(Calc3);

    var w = safePod1() ??
            safePod2() ??
            safePod3() ??
            throw new NoCalcsWorkedException(); // I've tested it on C# 7.2

    Console.Out.WriteLine($"result = {w}"); // w = 2.000001
}

private static double Calc1() => throw new Exception("Intentionally thrown exception");
private static double Calc2() => 2.000001;
private static double Calc3() => 3.000001;

Но что, если вы хотите создать безопасный модуль для результата ссылочного типа, возвращаемого функциями / методами CalcN ().

public static void Run()
{
    var safePod1 = SafePod.CreateForReferenceTypeResult(Calc1);
    var safePod2 = SafePod.CreateForReferenceTypeResult(Calc2);
    var safePod3 = SafePod.CreateForReferenceTypeResult(Calc3);

    User w = safePod1() ?? safePod2() ?? safePod3();

    if (w == null) throw new NoCalcsWorkedException();

    Console.Out.WriteLine($"The user object is {{{w}}}"); // The user object is {Name: Mike}
}

private static User Calc1() => throw new Exception("Intentionally thrown exception");
private static User Calc2() => new User { Name = "Mike" };
private static User Calc3() => new User { Name = "Alex" };

class User
{
    public string Name { get; set; }
    public override string ToString() => $"{nameof(Name)}: {Name}";
}

Итак, вы могли заметить, что нет необходимости «писать вспомогательный метод для каждого метода, который вы хотите использовать» .

Эти два типа стручков (для ValueTypeResults- и ReferenceTypeResultс) являются достаточно .


Вот код SafePod. Но это не контейнер. Вместо этого он создает безопасную в отношении исключений оболочку делегата для ValueTypeResults и ReferenceTypeResults.

public static class SafePod
{
    public static Func<TResult?> CreateForValueTypeResult<TResult>(Func<TResult> jobUnit) where TResult : struct
    {
        Func<TResult?> wrapperFunc = () =>
        {
            try { return jobUnit.Invoke(); } catch { return null; }
        };

        return wrapperFunc;
    }

    public static Func<TResult> CreateForReferenceTypeResult<TResult>(Func<TResult> jobUnit) where TResult : class
    {
        Func<TResult> wrapperFunc = () =>
        {
            try { return jobUnit.Invoke(); } catch { return null; }
        };

        return wrapperFunc;
    }
}

Вот как вы можете использовать оператор ??объединения с нулевым значением в сочетании с мощью первоклассных гражданских сущностей delegate.

AlexMelw
источник
0

Вы правы, оборачивая каждое вычисление, но вы должны заключать его в соответствии с принципом «говори-не-спрашивай».

double calc3WithConvertedException(){
    try { val = calc3(); }
    catch (Calc3Exception e3)
    {
        throw new NoCalcsWorkedException();
    }
}

double calc2DefaultingToCalc3WithConvertedException(){
    try { val = calc2(); }
    catch (Calc2Exception e2)
    {
        //defaulting to simpler method
        return calc3WithConvertedException();
    }
}


double calc1DefaultingToCalc2(){
    try { val = calc2(); }
    catch (Calc1Exception e1)
    {
        //defaulting to simpler method
        return calc2defaultingToCalc3WithConvertedException();
    }
}

Операции просты и могут независимо изменять свое поведение. И неважно, почему они по умолчанию. В качестве доказательства вы можете реализовать calc1DefaultingToCalc2 как:

double calc1DefaultingToCalc2(){
    try { 
        val = calc2(); 
        if(specialValue(val)){
            val = calc2DefaultingToCalc3WithConvertedException()
        }
    }
    catch (Calc1Exception e1)
    {
        //defaulting to simpler method
        return calc2defaultingToCalc3WithConvertedException();
    }
}
повышение стоимости
источник
-1

Похоже, ваши расчеты содержат более достоверную информацию, чем просто сам расчет. Возможно, для них было бы разумнее выполнить свою собственную обработку исключений и вернуть класс «результатов», содержащий информацию об ошибках, информацию о значениях и т. Д. Подумайте, как класс AsyncResult следует асинхронному шаблону. Затем вы можете оценить реальный результат расчета. Вы можете рационализировать это, думая о том, что если расчет не удастся, это так же информативно, как если бы он прошел. Следовательно, исключение - это информация, а не «ошибка».

internal class SomeCalculationResult 
{ 
     internal double? Result { get; private set; } 
     internal Exception Exception { get; private set; }
}

...

SomeCalculationResult calcResult = Calc1();
if (!calcResult.Result.HasValue) calcResult = Calc2();
if (!calcResult.Result.HasValue) calcResult = Calc3();
if (!calcResult.Result.HasValue) throw new NoCalcsWorkedException();

// do work with calcResult.Result.Value

...

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

Emoire
источник
Это нормально - аналогично тому, что предлагал OP в части выполнения вычислений. Я бы предпочел что-то вроде while (!calcResult.HasValue) nextCalcResult()списка Calc1, Calc2, Calc3 и т. Д.
Кирк Бродхерст
-3

А как насчет отслеживания ваших действий ...

double val;
string track = string.Empty;

try 
{ 
  track = "Calc1";
  val = calc1(); 

  track = "Calc2";
  val = calc2(); 

  track = "Calc3";
  val = calc3(); 
}
catch (Exception e3)
{
   throw new NoCalcsWorkedException( track );
}
Орн Кристьянссон
источник
4
Как это помогает? если calc1 () не работает, cals2 никогда не будет выполняться!
DeCaf
это не решает проблемы. Выполняйте calc1 только в случае сбоя calc2, выполняйте calc3 только при сбое calc1 && calc2.
Джейсон
+1 орн. Вот что я делаю. Мне нужно закодировать только один улов, сообщение, отправленное мне ( trackв данном случае), и я точно знаю , какой сегмент в моем коде привел к сбою блока. Может быть, вам следовало бы уточнить, чтобы сообщить таким участникам, как DeCaf, что trackсообщение отправляется вашей настраиваемой процедуре обработки ошибок, которая позволяет вам отлаживать ваш код. Похоже, он не понял твоей логики.
jp2code
Что ж, @DeCaf верен, мой сегмент кода не продолжает выполнять следующую функцию, о которой просил jjoelson, для моего решения это невозможно
Орн Кристьянссон