Есть ли разница между «throw» и «throw ex»? - PullRequest
388 голосов
/ 08 апреля 2009

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

Но мой вопрос отличается от того, что я называю «throw ex» в другой ошибке богоподобный метод обработки.

public class Program {
    public static void Main(string[] args) {
        try {
            // something
        } catch (Exception ex) {
            HandleException(ex);
        }
    }

    private static void HandleException(Exception ex) {
        if (ex is ThreadAbortException) {
            // ignore then,
            return;
        }
        if (ex is ArgumentOutOfRangeException) { 
            // Log then,
            throw ex;
        }
        if (ex is InvalidOperationException) {
            // Show message then,
            throw ex;
        }
        // and so on.
    }
}

Если бы Main использовалось в *1009*, то я бы использовал throw;, чтобы повторно выдать ошибку. Но в приведенном выше упрощенном коде все исключения проходят через HandleException

Имеет ли throw ex; тот же эффект, что и вызов throw при вызове внутри HandleException?

Ответы [ 10 ]

620 голосов
/ 08 апреля 2009

Да, есть разница;

  • throw ex сбрасывает трассировку стека (поэтому ваши ошибки могут появиться из HandleException)
  • throw нет - первоначальный преступник будет сохранен.

    static void Main(string[] args)
    {
        try
        {
            Method2();
        }
        catch (Exception ex)
        {
            Console.Write(ex.StackTrace.ToString());
            Console.ReadKey();
        }
    }
    
    private static void Method2()
    {
        try
        {
            Method1();
        }
        catch (Exception ex)
        {
            //throw ex resets the stack trace Coming from Method 1 and propogates it to the caller(Main)
            throw ex;
        }
    }
    
    private static void Method1()
    {
        try
        {
            throw new Exception("Inside Method1");
        }
        catch (Exception)
        {
            throw;
        }
    }
    
89 голосов
/ 22 апреля 2009

(я писал ранее, и @Marc Gravell исправил меня)

Вот демонстрация различия:

static void Main(string[] args) {
    try {
        ThrowException1(); // line 19
    } catch (Exception x) {
        Console.WriteLine("Exception 1:");
        Console.WriteLine(x.StackTrace);
    }
    try {
        ThrowException2(); // line 25
    } catch (Exception x) {
        Console.WriteLine("Exception 2:");
        Console.WriteLine(x.StackTrace);
    }
}

private static void ThrowException1() {
    try {
        DivByZero(); // line 34
    } catch {
        throw; // line 36
    }
}
private static void ThrowException2() {
    try {
        DivByZero(); // line 41
    } catch (Exception ex) {
        throw ex; // line 43
    }
}

private static void DivByZero() {
    int x = 0;
    int y = 1 / x; // line 49
}

и вот вывод:

Exception 1:
   at UnitTester.Program.DivByZero() in <snip>\Dev\UnitTester\Program.cs:line 49
   at UnitTester.Program.ThrowException1() in <snip>\Dev\UnitTester\Program.cs:line 36
   at UnitTester.Program.TestExceptions() in <snip>\Dev\UnitTester\Program.cs:line 19

Exception 2:
   at UnitTester.Program.ThrowException2() in <snip>\Dev\UnitTester\Program.cs:line 43
   at UnitTester.Program.TestExceptions() in <snip>\Dev\UnitTester\Program.cs:line 25

Вы можете видеть, что в Исключении 1 трассировка стека возвращается к методу DivByZero(), тогда как в Исключении 2 - нет.

Обратите внимание, что номер строки, показанный в ThrowException1() и ThrowException2(), является номером строки оператора throw, не номер строки вызова DivByZero(), что, вероятно, имеет смысл сейчас, когда я немного об этом думаю ...

Выход в режиме разблокировки

Исключение 1:

at ConsoleAppBasics.Program.ThrowException1()
at ConsoleAppBasics.Program.Main(String[] args)

Исключение 2:

at ConsoleAppBasics.Program.ThrowException2()
at ConsoleAppBasics.Program.Main(String[] args)

Поддерживает ли исходный stackTrace только в режиме отладки?

37 голосов
/ 27 июня 2012

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

Рассмотрим этот пример:

using System;

static class Program {
  static void Main() {
    try {
      ThrowTest();
    } catch (Exception e) {
      Console.WriteLine("Your stack trace:");
      Console.WriteLine(e.StackTrace);
      Console.WriteLine();
      if (e.InnerException == null) {
        Console.WriteLine("No inner exception.");
      } else {
        Console.WriteLine("Stack trace of your inner exception:");
        Console.WriteLine(e.InnerException.StackTrace);
      }
    }
  }

  static void ThrowTest() {
    decimal a = 1m;
    decimal b = 0m;
    try {
      Mult(a, b);  // line 34
      Div(a, b);   // line 35
      Mult(b, a);  // line 36
      Div(b, a);   // line 37
    } catch (ArithmeticException arithExc) {
      Console.WriteLine("Handling a {0}.", arithExc.GetType().Name);

      //   uncomment EITHER
      //throw arithExc;
      //   OR
      //throw;
      //   OR
      //throw new Exception("We handled and wrapped your exception", arithExc);
    }
  }

  static void Mult(decimal x, decimal y) {
    decimal.Multiply(x, y);
  }
  static void Div(decimal x, decimal y) {
    decimal.Divide(x, y);
  }
}

Если вы раскомментируете строку throw arithExc;, вы получите:

Handling a DivideByZeroException.
Your stack trace:
   at Program.ThrowTest() in c:\somepath\Program.cs:line 44
   at Program.Main() in c:\somepath\Program.cs:line 9

No inner exception.

Конечно, вы потеряли информацию о том, где произошло это исключение. Если вместо этого вы используете строку throw;, это то, что вы получите:

Handling a DivideByZeroException.
Your stack trace:
   at System.Decimal.FCallDivide(Decimal& d1, Decimal& d2)
   at System.Decimal.Divide(Decimal d1, Decimal d2)
   at Program.Div(Decimal x, Decimal y) in c:\somepath\Program.cs:line 58
   at Program.ThrowTest() in c:\somepath\Program.cs:line 46
   at Program.Main() in c:\somepath\Program.cs:line 9

No inner exception.

Это намного лучше, потому что теперь вы видите, что именно метод Program.Div вызывал у вас проблемы. Но все еще трудно понять, возникает ли эта проблема из строки 35 или строки 37 в блоке try.

Если вы используете третий вариант, заключая внешнее исключение, вы не теряете информацию:

Handling a DivideByZeroException.
Your stack trace:
   at Program.ThrowTest() in c:\somepath\Program.cs:line 48
   at Program.Main() in c:\somepath\Program.cs:line 9

Stack trace of your inner exception:
   at System.Decimal.FCallDivide(Decimal& d1, Decimal& d2)
   at System.Decimal.Divide(Decimal d1, Decimal d2)
   at Program.Div(Decimal x, Decimal y) in c:\somepath\Program.cs:line 58
   at Program.ThrowTest() in c:\somepath\Program.cs:line 35

В частности, вы можете видеть, что строка 35 приводит к проблеме. Тем не менее, для этого требуется, чтобы люди искали InnerException, и было бы несколько косвенным использовать внутренние исключения в простых случаях.

В этой записи блога они сохраняют номер строки (строку блока try), вызывая (через отражение) метод internal intance InternalPreserveStackTrace() объекта Exception. Но не рекомендуется использовать такое отражение (.NET Framework может когда-нибудь изменить их internal членов без предупреждения).

5 голосов
/ 05 декабря 2017

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

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

Давайте разберемся с примером.

Давай разберемся сначала, Брось.

static void Main(string[] args) {
    try {
        M1();
    } catch (Exception ex) {
        Console.WriteLine(" -----------------Stack Trace Hierarchy -----------------");
        Console.WriteLine(ex.StackTrace.ToString());
        Console.WriteLine(" ---------------- Method Name / Target Site -------------- ");
        Console.WriteLine(ex.TargetSite.ToString());
    }
    Console.ReadKey();
}

static void M1() {
    try {
        M2();
    } catch (Exception ex) {
        throw;
    };
}

static void M2() {
    throw new DivideByZeroException();
}

выход вышеупомянутого ниже.

показывает полную иерархию и имя метода, где на самом деле сгенерировано исключение .. это M2 -> M2. вместе с номерами строк

enter image description here

Во-вторых .. давайте разберемся, бросив экс. Просто замените throw на throw ex в блоке catch метода M2. как показано ниже.

enter image description here

вывод команды throw ex code такой, как показано ниже.

enter image description here

Вы можете увидеть разницу в выходных данных. Throw ex просто игнорирует всю предыдущую иерархию и сбрасывает трассировку стека с помощью строки / метода, в который записывается throw ex.

4 голосов
/ 08 апреля 2009

Когда вы выполняете throw ex, выброшенное исключение становится «оригинальным». Таким образом, все предыдущие трассировки стека не будут там.

Если вы бросите, исключение просто перейдет на вниз по строке , и вы получите полный след стека.

4 голосов
/ 08 апреля 2009

Нет, это приведет к тому, что у исключения будет другая трассировка стека. Только использование throw без какого-либо исключения объекта в обработчике catch оставит трассировку стека без изменений.

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

3 голосов
/ 06 апреля 2018

MSDN обозначает :

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

1 голос
/ 19 июля 2014

Посмотрите здесь: http://blog -mstechnology.blogspot.de / 2010/06 / throw-vs-throw-ex.html

Throw

try 
{
    // do some operation that can fail
}
catch (Exception ex)
{
    // do some local cleanup
    throw;
}

Сохраняет информацию стека с исключением

Это называется "Ретроу"

Если хотите создать новое исключение,

throw new ApplicationException("operation failed!");

Бросок Ex :

try
{
    // do some operation that can fail
}
catch (Exception ex)
{
    // do some local cleanup
    throw ex;
}

Не отправляется информация о стеке с исключением

Это называется "Разбить стек"

Если хотите создать новое исключение,

throw new ApplicationException("operation failed!",ex);
0 голосов
/ 01 февраля 2017

Чтобы по-другому взглянуть на это, использование throw особенно полезно, если вы предоставляете клиенту API и хотите предоставить подробную информацию трассировки стека для своей внутренней библиотеки. Используя здесь throw, я бы получил трассировку стека в этом случае библиотеки System.IO.File для File.Delete. Если я использую throw ex, то эта информация не будет передана моему обработчику.

static void Main(string[] args) {            
   Method1();            
}

static void Method1() {
    try {
        Method2();
    } catch (Exception ex) {
        Console.WriteLine("Exception in Method1");             
    }
}

static void Method2() {
    try {
        Method3();
    } catch (Exception ex) {
        Console.WriteLine("Exception in Method2");
        Console.WriteLine(ex.TargetSite);
        Console.WriteLine(ex.StackTrace);
        Console.WriteLine(ex.GetType().ToString());
    }
}

static void Method3() {
    Method4();
}

static void Method4() {
    try {
        System.IO.File.Delete("");
    } catch (Exception ex) {
        // Displays entire stack trace into the .NET 
        // or custom library to Method2() where exception handled
        // If you want to be able to get the most verbose stack trace
        // into the internals of the library you're calling
        throw;                
        // throw ex;
        // Display the stack trace from Method4() to Method2() where exception handled
    }
}
0 голосов
/ 01 декабря 2016
int a = 0;
try {
    int x = 4;
    int y ;
    try {
        y = x / a;
    } catch (Exception e) {
        Console.WriteLine("inner ex");
        //throw;   // Line 1
        //throw e;   // Line 2
        //throw new Exception("devide by 0");  // Line 3
    }
} catch (Exception ex) {
    Console.WriteLine(ex);
    throw ex;
}
  1. если прокомментированы все строки 1, 2 и 3 - Выход - внутренний ex

  2. если прокомментированы все строки 2 и 3 - Выход - внутренний экс System.DevideByZeroException: {"Попытка деления на ноль."} ---------

  3. если все строки 1 и 2 прокомментированы - Выход - внутренний экс System.Exception: делится на 0 ----

  4. если прокомментированы все строки 1 и 3 - Выход - внутренний экс System.DevideByZeroException: {"Попытка деления на ноль."} ---------

и StackTrace будет сброшен в случае броска ex;

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