Есть ли разница между "броском" и "броском"?

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

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

Ответ 1

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

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

Ответ 2

(Я опубликовал ранее, и @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

Вы можете видеть, что в Exception 1 трассировка стека возвращается к методу DivByZero(), тогда как в Exception 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)

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

Ответ 3

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

Рассмотрим следующий пример:

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 когда-нибудь без предупреждения).

Ответ 4

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

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

Ответ 5

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

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

Ответ 6

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

Просто, чтобы дать обзор этих двух терминов, throw и throw 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, как показано ниже.

enter image description here

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

Ответ 7

MSDN означает:

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

Ответ 8

Посмотрите здесь: 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;
   }

Сохраняет информацию о стеке с помощью Exception

Это называется "Rethrow"

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

throw new ApplicationException("operation failed!");

Throw Ex:

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

Он не будет отправлять информацию стека с исключением

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

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

throw new ApplicationException("operation failed!",ex);

Ответ 9

Чтобы дать вам другую точку зрения на это, использование 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
        }
    }

Ответ 10

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, 2 и 3 прокомментированы - Выход - внутренний ex

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

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

  • если все строки 1 и 3 прокомментированы - Выход - внутренний         System.DevideByZeroException: { "Попытка деления на ноль".} --------- и StackTrace будет reset в случае throw ex;