“Fırlat” ve “fırlat” arasında bir fark var mı?


437

Bu ikisi arasındaki farkın zaten ne olduğunu soran bazı mesajlar var.
(neden bundan bahsetmek zorundayım ...)

Ama sorum başka bir hata tanrı benzeri taşıma yönteminde "ex ex" diyorum bir şekilde farklıdır .

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.
    }
}

Eğer try & catchkullanılmış Mainolsaydı throw;, hatayı yeniden düzenlemek için kullanırdım . Ancak yukarıdaki basit kodda, tüm istisnalar geçerHandleException

Mu throw ex;arayarak aynı etkiye sahiptir throwiçeride aradığında HandleException?


3
Bir fark var, yığın izlemesinin istisnada olup olmadığı veya nasıl olduğu ile ilgili, ama şu anda hangisinin hangisi olduğunu hatırlamıyorum, bu yüzden bu cevabı listelemeyeceğim.
Joel Coehoorn

@Joel: Teşekkürler. HandleError istisnasını kullanmak kötü bir fikir. Sadece bazı hata işleme kodlarını yeniden düzenlemek istedim.
dance2die

1
Üçüncü yol yeni bir istisna sarmak ve timwise.blogspot.co.uk/2014/05/…
Tim Abell

Yanıtlar:


679

Evet, bir fark var;

  • throw exyığın izlemesini sıfırlar (böylece hatalarınız kaynaklanıyor gibi görünür HandleException)
  • throw olmaz - asıl suçlu korunur.

    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;
        }
    }
    

28
Marc'ın cevabını biraz daha genişletmek için daha fazla ayrıntıyı burada bulabilirsiniz: geekswithblogs.net/sdorman/archive/2007/08/20/…
Scott Dorman

3
@Shaul; hayır değil. Yayınınıza bir yorumda ayrıntı verdim.
Marc Gravell

1
@Marc Gravell - özür dilerim, haklıydın. Downvot için üzgünüm; geri almak benim için çok geç ... :(
Shaul Behr

3
@Marc: Atış SADECE ilk istisnanın atıldığı yöntemde değilse orijinal suçluyu koruyor gibi görünüyor (bu soruya bakın: stackoverflow.com/questions/5152265/… )
Brann

3
@ScottDorman Görünüşe göre bağlantınız bir blog taşıma işleminden sonra doğru yönlendirilmiyor. Şimdi burada yaşıyor gibi görünüyor . Düzenleme: Hey, bekle, bu senin blogun! Kendi bağlantılarınızı düzeltin! ; ^ D
21'de ruffin

96

(Daha önce yayınladım ve @Marc Gravell beni düzeltti)

İşte farkın bir gösterimi:

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
}

ve işte çıktı:

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

İstisna 1'de yığın izlemesinin DivByZero()yönteme geri döndüğünü , buna karşılık İstisna 2'de olmadığını görebilirsiniz.

Satır numarası gösterilen olsa da, dikkat edin ThrowException1()ve ThrowException2()satır numarasıdır throwaçıklamada, değil için çağrının satır numarası DivByZero()muhtemelen şimdi biraz düşünmek mantıklı, ...

Serbest bırakma modunda çıkış

İstisna 1:

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

İstisna 2:

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

Orijinal stackTrace'i yalnızca hata ayıklama modunda mı tutuyor?


1
Bunun nedeni, derleyicinin optimizasyon işleminin kısa yöntemler gibi sıralanmasıdır DevideByZero, böylece yığın izlemesi aynıdır. belki bunu kendi başına bir soru olarak göndermelisin
Menahem

42

Diğer cevaplar tamamen doğru, ama bu cevap bazı ekstra detaliler sağlıyor, sanırım.

Bu örneği düşünün:

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;Çizgiyi kaldırırsanız, çıktınız:

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.

Elbette, bu istisnanın nerede olduğu hakkında bilgi kaybettiniz. Bunun yerine throw;satırı kullanırsanız, elde ettiğiniz şey budur:

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.

Bu çok daha iyi, çünkü şimdi Program.Divsize sorunlara neden olan yöntem olduğunu görüyorsunuz. Ancak bu sorunun trybloktaki 35. satırdan mı yoksa 37. satırdan mı kaynaklandığını görmek hala zor .

Dış bir istisnayı içine alarak üçüncü alternatifi kullanırsanız hiçbir bilgi kaybetmezsiniz:

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

Özellikle , soruna yol açan 35. satır olduğunu görebilirsiniz . Bununla birlikte, bu, insanların arama yapmasını gerektirir InnerExceptionve basit durumlarda iç istisnaları kullanmak biraz dolaylı hisseder.

Gelen bu blog yayınında onlar (yansıma yoluyla) arayarak hat numarasını (try'ın hattı) korumak internalintance yöntemi InternalPreserveStackTrace()üzerinde Exceptionnesne. Ancak böyle bir yansıma kullanmak hoş değil (.NET Framework, internalüyelerini uyarı yapmadan bir gün değiştirebilir ).


6

ata ve ata arasındaki farkı anlayalım. Birçok .net röportajında ​​bu ortak sorunun sorulduğunu duydum.

Sadece bu iki terime genel bir bakış vermek için, atma ve atma ex özel durumun nerede olduğunu anlamak için kullanılır. Throw ex, atılan yere bakılmaksızın, istisna yığın yığınını yeniden yazar.

Bir örnekle anlayalım.

Önce Throw'u anlayalım.

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();
}

yukarıdakilerin çıktısı aşağıdadır.

istisna fırlattığı tam hiyerarşi ve yöntem adını gösterir .. M2 -> M2'dir. satır numaraları ile birlikte

resim açıklamasını buraya girin

İkincisi .. atarak ex anlayalım. M2 yöntemi yakalama bloğundaki atımı ex ile değiştirin. aşağıda olduğu gibi.

resim açıklamasını buraya girin

çıkış ex kodu çıkışı aşağıdaki gibidir ..

resim açıklamasını buraya girin

Çıktıdaki farkı görebilirsiniz.


5

Bunu yaptığınızda throw ex, atılan istisna "orijinal" olur. Yani önceki tüm yığın izi orada olmayacak.

Bunu yaparsanız throw, istisna satırdan aşağı iner ve tüm yığın izlemesini alırsınız.


4

Hayır, bu kural dışı durumun farklı bir yığın izine sahip olmasına neden olur. Yalnızca işleyicide throwherhangi bir istisna olmadan bir nesne kullanılması catchyığın izini değiştirmeden bırakır.

Kural dışı durumun yeniden oluşturulup oluşturulmayacağı konusunda HandleException öğesinden bir boole döndürmek isteyebilirsiniz.


4

MSDN ne anlama geliyor :

Bir istisna atıldığında, taşıdığı bilgilerin bir kısmı yığın izlemedir. Yığın izleme, kural dışı durumu atan yöntemle başlayan ve kural dışı durumu yakalayan yöntemle biten yöntem çağrısı hiyerarşisinin bir listesidir. Throw deyiminde istisna belirtilerek bir kural dışı durum atılırsa, yığın izlemesi geçerli yöntemde yeniden başlatılır ve kural dışı durumu atan orijinal yöntem ile geçerli yöntem arasındaki yöntem çağrıları listesi kaybolur. Özgün yığın izleme bilgilerini istisna dışında tutmak için, istisna belirtmeden throw deyimini kullanın.


2

Buraya bakın: http://blog-mstechnology.blogspot.de/2010/06/throw-vs-throw-ex.html

Atmak :

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

İstisna ile Yığın bilgilerini korur

Buna "Tekrar" denir

Yeni bir istisna atmak istiyorsanız,

throw new ApplicationException("operation failed!");

Ex atmak :

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

İstisna ile Yığın Bilgilerini Göndermez

Buna "Yığını Kırmak" denir

Yeni bir istisna atmak istiyorsanız,

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

0

Bu konuda size farklı bir bakış açısı sağlamak için, istemciye bir API sağlıyorsanız ve dahili kitaplığınız için ayrıntılı yığın izleme bilgileri sağlamak istiyorsanız, throw kullanımı özellikle yararlıdır. Burada atmak kullanarak, File.Delete için System.IO.File kitaplığı bu durumda yığın izleme olsun. Eğer eskiyi atma kullanırsam, bu bilgi işleyicime aktarılmaz.

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
    }
}

-1
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. tüm Satır 1, 2 ve 3 yorumlanırsa - Çıktı - iç ex

  2. tüm Satır 2 ve 3 yorumlanırsa - Çıktı - iç System.DevideByZeroException: {"Sıfıra bölünmeye çalışıldı."} ---------

  3. tüm Satır 1 ve 2 yorumlanırsa - Çıktı - iç Sistem üzerinden. istisna: 0 ----

  4. tüm Satır 1 ve 3 yorumlanırsa - Çıktı - iç System.DevideByZeroException: {"Sıfıra bölünmeye çalışıldı."} ---------

ve atma durumunda StackTrace sıfırlanacaktır;

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.