Разница между перебрасыванием параметра без улова и бездействием? - PullRequest
14 голосов
/ 04 апреля 2009

Предположим, у меня есть два следующих класса в двух разных сборках:

//in assembly A
public class TypeA {
   // Constructor omitted
   public void MethodA
   {
     try {
       //do something
     }
     catch {
        throw;
     }
   }
}
//in assembly B
public class TypeB {
   public void MethodB
   {
     try {
       TypeA a = new TypeA();
       a.MethodA();
     }
     catch (Exception e)
       //Handle exception
     }
   }
}

В этом случае try-catch в MethodA просто повышает исключение, но на самом деле не обрабатывает его. Есть ли какое-либо преимущество в использовании try-catch вообще в MethodA? Другими словами, есть ли разница между этим типом блока try-catch и его отсутствием?

Ответы [ 11 ]

7 голосов
/ 04 апреля 2009

В вашем примере это не дает никаких преимуществ. Но есть случаи, когда желательно просто всплыть конкретное исключение.

    public void Foo()
    {
        try
        {
            // Some service adapter code

            // A call to the service
        }
        catch (ServiceBoundaryException)
        {
            throw;
        }
        catch (Exception ex)
        {
            throw new AdapterBoundaryException("some message", ex);
        }
    }

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

3 голосов
/ 04 апреля 2009

Принятие как есть, первый вариант может показаться плохой (или это может быть «бесполезной»?) Идеей. Тем не менее, это редко делается таким образом. Исключения перебрасываются изнутри блока Catch обычно при двух условиях:

а. Вы хотите проверить сгенерированное для данных исключение и условно скопировать его в стек.

try 
{
  //do something
}
catch (Exception ex)
{
  //Check ex for certain conditions.
  if (ex.Message = "Something bad")
    throw ex;
  else
    //Handle the exception here itself.
}

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

try 
{
  //do something
}
catch (StackOverflowException ex)
{
    //Bubble up the exception to calling code 
    //by wrapping it up in a custom exception.
    throw new MyEuphemisticException(ex, "Something not-so-good just happened!");
}
3 голосов
/ 04 апреля 2009

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

Если вы не поймаете это, то в итоге вы получите необработанное исключение, которое вызовет какую-то диагностику (например, отладчик или регистратор исключений), будет доступен полный стек и его состояние в фактической точке отказа. для осмотра.

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

2 голосов
/ 04 апреля 2009

Просто повторное бросание не имеет смысла - это так же, как если бы вы ничего не делали.

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

2 голосов
/ 04 апреля 2009

С кодом, который вы написали для MethodA, нет никакой разницы. Все, что он будет делать, это съесть процессорные циклы. Однако при написании кода таким способом может быть преимущество, если есть ресурс, который вы должны освободить. Например

Resource r = GetSomeResource();
try {
  // Do Something
} catch { 
  FreeSomeResource();
  throw;
}
FreeSomeResource();

Однако нет смысла делать это таким образом. Было бы намного лучше просто использовать блок finally.

1 голос
/ 05 апреля 2009

Поскольку классы находятся в 2 разных сборках, вы можете захотеть просто перехватить исключение для его регистрации, а затем выбросить его обратно вызывающей стороне, чтобы он мог обрабатывать его так, как считает нужным. Бросок вместо броска ex сохранит контекстную информацию о том, откуда возникло исключение. Это может оказаться полезным, когда ваша сборка представляет собой API / framework, где вы никогда не должны проглатывать исключения, если это не имеет смысла, но полезно, тем не менее, при устранении неполадок, если она регистрируется, например, в EventLog.

1 голос
/ 04 апреля 2009

Сборка A - try catch - block для меня не имеет никакого смысла. Я полагаю, что если вы не собираетесь обрабатывать исключение, то почему вы ловите эти исключения ... В любом случае оно будет брошено на следующий уровень.

Но если вы создаете API среднего уровня или что-то в этом роде и обрабатываете исключение (и, следовательно, уничтожаете исключение) на этом уровне, не имеет смысла, тогда вы можете создать свой собственный уровень ApplicationException. Но отбрасывать одно и то же исключение не имеет смысла.

1 голос
/ 04 апреля 2009

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

public class XmlException: Exception{
   ....
} 

public class XmlParser{
   public void Parse()
   {
      try{
          ....
      }
      catch(IOException ex)
      {
         throw new XmlException("IO Error while Parsing", ex );
      }
   }
}

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

1 голос
/ 04 апреля 2009

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

1 голос
/ 04 апреля 2009

Никогда не используйте вариант A. Как говорит Антон, он поглощает трассировку стека. Пример JaredPar также съедает трассировку стека. Лучшее решение было бы:

SomeType* pValue = GetValue();
try {
  // Do Something
} finally {
  delete pValue;
}

Если у вас есть что-то в C #, которое нужно выпустить, например, FileStream, у вас есть два следующих варианта:

FileStream stream;
try
{
  stream = new FileStream("C:\\afile.txt");
  // do something with the stream
}
finally
{
  // Will always close the stream, even if there are an exception
  stream.Close(); 
}

или более чисто:

using (FileStream stream = new FileStream("c:\\afile.txt"))
{
  // do something with the stream
}

Оператор using удалит (и закроет) поток по завершении или при закрытии исключения.

...