Есть ли разница между «throw» и «throw ex»?

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

Но мой вопрос отличается от того, что я называю «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.
    }
}

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

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

8.04.2009 14:22:12
Есть разница, это связано с тем, появляется ли или как трассировка стека в исключении, но я не помню, какой именно сейчас, поэтому я не буду перечислять этот ответ.
Joel Coehoorn 8.04.2009 14:24:44
@Joel: спасибо. Я предполагаю, что использование исключения HandleError - плохая идея. Я просто хотел изменить код обработки ошибок.
dance2die 8.04.2009 14:27:35
Третий способ - завернуть в новое исключение и перезапустить timwise.blogspot.co.uk/2014/05/…
Tim Abell 10.05.2014 10:38:20
Michael Freidgeim 7.03.2016 07:28:00
10 ОТВЕТОВ
РЕШЕНИЕ

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

  • 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;
        }
    }
672
23.11.2018 10:23:44
Чтобы немного расширить ответ Марка, вы можете найти более подробную информацию здесь: geekswithblogs.net/sdorman/archive/2007/08/20/…
Scott Dorman 8.04.2009 14:38:48
@Shaul; нет, это не так Я дал подробности в комментарии к вашему посту.
Marc Gravell♦ 22.04.2009 12:05:09
@Marc Gravell - мои извинения, вы были правы. Извините за понижение голосов; мне уже поздно отменять ... :(
Shaul Behr 22.04.2009 18:08:22
@Marc: Кажется, что throw сохраняет первоначального нарушителя ТОЛЬКО в том случае, если бросок не относится к методу, в котором было сгенерировано первоначальное исключение (см. Этот вопрос: stackoverflow.com/questions/5152265/… )
Brann 1.03.2011 09:17:14
@ScottDorman Похоже, ваша ссылка неправильно пересылается после переноса блога. Похоже, сейчас он живет здесь . Редактировать: Эй, подождите, это ваш блог! Исправьте свои собственные ссылки! ; ^ D
ruffin 8.10.2019 21:21:22

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

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

4
8.04.2009 14:26:59

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

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

4
29.05.2015 08:54:39

(Я отправил ранее, и @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 только в режиме отладки?

95
25.10.2018 22:10:02
Это потому, что процесс оптимизации компилятора включает в себя короткие методы, такие как DevideByZero, поэтому трассировка стека одинакова. может быть, вы должны опубликовать это как вопрос самостоятельно
Menahem 8.09.2016 14:23:03

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

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

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методе входа в инстансе InternalPreserveStackTrace()на Exceptionобъекте. Но не очень хорошо использовать такое отражение (.NET Framework может изменить своих internalчленов однажды без предупреждения).

40
25.10.2018 22:21:48

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

Бросить :

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);
2
25.10.2018 22:13:53
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 - Выход - внутренний экс

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

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

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

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

-1
25.10.2018 22:20:01

Чтобы дать вам другое представление об этом, использование 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
25.10.2018 22:15:11

давайте поймем разницу между броском и броском экс. Я слышал, что во многих .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. вместе с номерами строк

введите описание изображения здесь

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

введите описание изображения здесь

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

введите описание изображения здесь

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

6
25.10.2018 22:11:02

MSDN означает :

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

4
6.04.2018 13:30:18