C#: Обработка исключений в рекурсивном вызове

Да, Вы корректны - создают файл ресурсов. Когда Вы делаете это, Вы не должны "загружать" строку, на нее сошлются как Ресурс. WhateverStringYouDefined.

6
задан Stefan Steinegger 19 October 2009 в 13:11
поделиться

5 ответов

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

А как насчет того, чтобы попробовать это?

public void Recursive(int x)
{
  try
  {
    _Recursive(x)
  }
  catch
  { 
    throw new RecursionException(path.ToString(), ex);
    clear path, we know we are at the top at this point
  }
}

private void _Recursive(int x)
{
    // maintain the recursion path information
    path.Push(x);

    _Recursive(x + 6);

    //maintain the recursion path information
    //note this is not in a catch so will not be called if there is an exception
    path.Pop()
}

Если вы используете потоки и т. Д., Вам, возможно, придется посмотреть на сохранение пути в локальном хранилище потока.


Если вы не хотите заставлять вызывающего абонента иметь дело с RecursionException, вы можете сделать «путь» общедоступным, чтобы вызывающий мог получить к нему доступ. (Как позже ответил Эрик Липперт)

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

public void Recursive(int x)
{
  try
  {
    _Recursive(x)
  }
  catch
  { 
    //Log the path to your loggin sysem of choose
    //Maybe log the exception if you are not logging at the top 
    //   of your applicatoin         
    //Clear path, we know we are at the top at this point
  }
}

Это имеет то преимущество, что вызывающему абоненту вообще не нужно знать о «пути».

Все сводится к тому, что нужно вашему вызывающему,

2
ответ дан 8 December 2019 в 16:05
поделиться

Ваша проблема заключается в обработке исключений. Как правило, перенос исключения в собственное исключение является плохой идеей, потому что он накладывает бремя на вызывающего пользователя вашего кода, который вынужден обрабатывать ваше исключение. Вызывающий, который подозревает, что они могут, скажем, вызвать исключение «путь не найден», вызывая ваш код, не может заключить свой вызов в try-catch, который перехватывает IOException. Они должны перехватить ваше исключение RecursionException, а затем написать кучу кода, чтобы опросить его, чтобы определить, что это за исключение на самом деле. Бывают случаи, когда этот шаблон оправдан, но я не вижу, чтобы это один из них.

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

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

Хорошо, отлично, если это цели дизайна затем реализуйте это:

class C
{
  private Stack<int> path

#if DEBUG

    = new Stack<int>();

#else

    = null;

#endif

  public Stack<int> Path { get { return path; } }

  [Conditional("DEBUG")] private void Push(int x) { Path.Push(x); }
  [Conditional("DEBUG")] private void Pop() { Path.Pop(); }
  public int Recursive(int n)
  { 
    Push(n);
    int result = 1;
    if (n > 1)
    {
      result = n * Recursive(n-1);
      DoSomethingDangerous(n);
    }
    Pop();
    return result;
  }
}

И теперь вызывающий может справиться с этим:

C c = new C();
try
{
  int x = c.Recursive(10);
}
catch(Exception ex)
{

#if DEBUG

  // do something with c.Path

Вы видите, что мы здесь делаем? Мы пользуемся преимуществом того факта, что исключение останавливает рекурсивный алгоритм на своем пути. Последнее, что мы хотим сделать, это очистить путь, добавив finally; мы хотим, чтобы всплывающие окна не отображались при исключении!

Имеет смысл?

class C
{
  private Stack<int> path

#if DEBUG

    = new Stack<int>();

#else

    = null;

#endif

  public Stack<int> Path { get { return path; } }

  [Conditional("DEBUG")] private void Push(int x) { Path.Push(x); }
  [Conditional("DEBUG")] private void Pop() { Path.Pop(); }
  public int Recursive(int n)
  { 
    Push(n);
    int result = 1;
    if (n > 1)
    {
      result = n * Recursive(n-1);
      DoSomethingDangerous(n);
    }
    Pop();
    return result;
  }
}

И теперь вызывающий абонент может с этим справиться:

C c = new C();
try
{
  int x = c.Recursive(10);
}
catch(Exception ex)
{

#if DEBUG

  // do something with c.Path

Вы видите, что мы здесь делаем? Мы пользуемся преимуществом того факта, что исключение останавливает рекурсивный алгоритм на своем пути. Последнее, что мы хотим сделать, это очистить путь, добавив finally; мы хотим, чтобы всплывающие окна не отображались при исключении!

Имеет смысл?

class C
{
  private Stack<int> path

#if DEBUG

    = new Stack<int>();

#else

    = null;

#endif

  public Stack<int> Path { get { return path; } }

  [Conditional("DEBUG")] private void Push(int x) { Path.Push(x); }
  [Conditional("DEBUG")] private void Pop() { Path.Pop(); }
  public int Recursive(int n)
  { 
    Push(n);
    int result = 1;
    if (n > 1)
    {
      result = n * Recursive(n-1);
      DoSomethingDangerous(n);
    }
    Pop();
    return result;
  }
}

И теперь вызывающий абонент может с этим справиться:

C c = new C();
try
{
  int x = c.Recursive(10);
}
catch(Exception ex)
{

#if DEBUG

  // do something with c.Path

Вы видите, что мы здесь делаем? Мы пользуемся преимуществом того факта, что исключение останавливает рекурсивный алгоритм на своем пути. Последнее, что мы хотим сделать, это очистить путь, добавив finally; мы хотим, чтобы всплывающие окна не отображались при исключении!

Имеет смысл?

4
ответ дан 8 December 2019 в 16:05
поделиться

Просто упрощаю (немного) обработку исключений:

void Recursive(int x)
{
    // maintain the recursion path information
    path.Push(x);

    try
    {
        // do some stuff and recursively call the method
        Recursive(x + 6);
    }
    catch( RecursionException )
    {
        throw;
    }
    catch( Exception )
    {
        throw new RecursionException(path.ToString(), ex);
    }
    finally
    {
        // maintain the recursion path information
        path.Pop()
    }
}

Вы получаете информацию о стеке вызовов с помощью Exception, если она вам нужна, кроме того, вы можете написать это как фрагмент, а затем просто вставить это там, где вам нужно для повторного использования.

Также существует следующая возможность, которая будет медленной, но должна работать:

void DoStuff()
{
    this.Recursive(1, this.RecursiveFunction1);
    this.Recursive(2, this.RecursiveFunction2);
}

bool RecursiveFunction1(int x)
{
    bool continueRecursing = false;

    // do some stuff
    return continueRecursing;
}

bool RecursiveFunction2(int y)
{
    bool continueRecursing = false;

    // do some other stuff here
    return continueRecursing;
}

private void Recursive(int x, Func<int, bool> actionPerformer)
{
    // maintain the recursion path information
    path.Push(x);

    try
    {
        // recursively call the method
        if( actionPerformer(x) )
        {
            Recursive(x + 6, actionPerformer);
        }
    }
    catch( RecursionException )
    {
        throw;
    }
    catch( Exception ex )
    {
        throw new RecursionException(path.ToString(), ex);
    }
    finally
    {
        // maintain the recursion path information
        path.Pop();
    }
}
5
ответ дан 8 December 2019 в 16:05
поделиться

А как насчет того, чтобы выдернуть обработчик catch из рекурсивной функции и просто записать рекурсию без меньшей обработки?

void StartRecursion(int x)
{
    try
    {
        path.Clear();
        Recursive(x);
    }
    catch (Exception ex)
    {
        throw new RecursionException(path.ToString(), ex);
    }
}

void Recursive(int x)
{
    path.Push(x);
    Recursive(x + 6);
    path.Pop();
}

void Main()
{
    StartRecursion(100);
}
4
ответ дан 8 December 2019 в 16:05
поделиться
void Recursive(int x)
{
  // maintain the recursion path information
  path.Push(x);

  try
  {
    // do some stuff and recursively call the method
    Recursive(x + 6);
  }
  finally
  {
    // maintain the recursion path information
    path.Pop()
  }
}

void Recursive2(int x)
{
  try
  {
     Recursive(x);
  }
  catch()
  {
      // Whatever
  }
}

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

0
ответ дан 8 December 2019 в 16:05
поделиться
Другие вопросы по тегам:

Похожие вопросы: