क्या "थ्रो" और "थ्रो एक्स" के बीच अंतर है?


437

कुछ पोस्ट हैं जो पूछते हैं कि उन दोनों के बीच अंतर पहले से ही क्या है।
(मुझे इसका उल्लेख क्यों करना है ...)

लेकिन मेरा सवाल एक तरह से अलग है कि मैं एक और त्रुटि भगवान-जैसे हैंडलिंग पद्धति में "थ्रो एक्स" कह रहा हूं ।

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?


3
एक अंतर है, यह है कि स्टैक ट्रेस अपवाद में दिखाई देता है या नहीं, लेकिन मुझे यह याद नहीं है कि अभी कौन सा है, इसलिए मैं इसका उत्तर नहीं दूंगा।
जोएल कोएहॉर्न

@Joel: धन्यवाद। मुझे लगता है कि HandleError अपवाद का उपयोग करना एक बुरा विचार है। मैं सिर्फ कोड को संभालने में कुछ त्रुटि सुधारना चाहता था।
डांस

1
तीसरा तरीका एक नए अपवाद में लपेटना है और टाइमवाइज rebrow.blogspot.co.uk/2014/05/…
टिम एबेल

जवाबों:


679

हां, वहां एक अंतर है;

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

28
मार्क के उत्तर पर थोड़ा विस्तार करने के लिए, आप यहाँ और अधिक विवरण पा सकते हैं: geekswithblogs.net/sdorman/archive/2007/08/20/…/
स्कॉट डोरमैन

3
@Shaul; नहीं, यह नहीं है। मैंने आपकी पोस्ट पर एक टिप्पणी में विवरण दिया है।
मार्क Gravell

1
@ मेरी ग्रेवेल - मेरी क्षमायाचना, आप सही थे। नीचे के बारे में क्षमा करें; यह मेरे लिए पूर्ववत करने के लिए बहुत देर हो चुकी है ...:
शाऊल बेहार

3
@Marc: ऐसा लगता है कि फेंक मूल अपराधी को केवल तभी सुरक्षित रखता है जब फेंक उस पद्धति में न हो जिसमें प्रारंभिक अपवाद फेंका गया हो (यह प्रश्न देखें: stackoverflow.com/questions/5152265/… )
Brann

3
@ScottDorman लगता है कि आपके लिंक को ब्लॉग माइग्रेशन के बाद सही तरीके से अग्रेषित नहीं किया जा रहा है। लगता है अब यहीं रहता हैसंपादित करें: अरे, रुको, यह आपका ब्लॉग है! अपने स्वयं के लिंक को ठीक करें! ; ^ डी
रफिन

96

(मैंने पहले पोस्ट किया था, और @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)

क्या यह केवल डिबग मोड में मूल स्टैकट्रेस बनाए रखता है?


1
इसकी वजह यह है कि कंपाइलर की ऑप्टिमाइज़ेशन प्रक्रिया छोटे तरीकों को रेखांकित करती है DevideByZero, इसलिए स्टैक ट्रेस आईएस ही है। हो सकता है कि आपको यह एक सवाल के रूप में पोस्ट करना चाहिए
Menahem

42

अन्य उत्तर पूरी तरह से सही हैं, लेकिन यह उत्तर कुछ अतिरिक्त डिटेल प्रदान करता है, मुझे लगता है।

इस उदाहरण पर विचार करें:

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तरीका था जिससे आपको परेशानी हुई। लेकिन यह देखना अभी भी मुश्किल है कि यह समस्या tryब्लॉक में लाइन 35 या लाइन 37 से आती है या नहीं ।

यदि आप तीसरे विकल्प का उपयोग करते हैं, तो बाहरी अपवाद में लपेटकर, आप कोई जानकारी नहीं खोते हैं:

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, और यह साधारण मामलों में आंतरिक अपवादों का उपयोग करने के लिए कुछ हद तक अप्रत्यक्ष लगता है।

में इस ब्लॉग पोस्ट वे बुला (प्रतिबिंब के माध्यम से) से लाइन नंबर (कोशिश ब्लॉक की लाइन) को बचाए रखने internalintance विधि InternalPreserveStackTrace()पर Exceptionवस्तु। लेकिन इस तरह प्रतिबिंब का उपयोग करना अच्छा नहीं है (.NET फ्रेमवर्क अपने internalसदस्यों को किसी दिन चेतावनी के बिना बदल सकता है )।


6

चलो फेंक और पूर्व के बीच का अंतर समझते हैं। मैंने सुना है कि कई .net साक्षात्कारों में यह आम पूछा जा रहा है।

इन दो शब्दों का अवलोकन देने के लिए, थ्रो और थ्रो एक्स दोनों का उपयोग यह समझने के लिए किया जाता है कि अपवाद कहां हुआ है। थ्रो पूर्व को अपवाद के स्टैक ट्रेस को फिर से लिखे चाहे जो वास्तव में फेंक दिया गया हो।

आइए एक उदाहरण से समझते हैं।

पहले थ्रो को समझते हैं।

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

उपरोक्त का उत्पादन नीचे है।

पूरी पदानुक्रम और विधि का नाम दिखाता है जहां वास्तव में अपवाद फेंक दिया गया है .. यह एम 2 -> एम 2 है। लाइन नंबरों के साथ

यहां छवि विवरण दर्ज करें

दूसरी बात .. थ्रो एक्स से समझने की सुविधा देता है। बस एम 2 मेथड कैच ब्लॉक में थ्रो एक्स के साथ फेंक दें। नीचे के अनुसार।

यहां छवि विवरण दर्ज करें

फेंक पूर्व कोड के उत्पादन के रूप में नीचे है ..

यहां छवि विवरण दर्ज करें

आप आउटपुट में अंतर देख सकते हैं .. थ्रो एक्स केवल पिछले सभी पदानुक्रम को अनदेखा करता है और लाइन / विधि के साथ स्टैक ट्रेस को रीसेट करता है जहां थ्रो एक्स लिखा जाता है।


5

जब आप ऐसा करते हैं throw ex, तो वह फेंका गया अपवाद "मूल" बन जाता है। तो पिछले सभी स्टैक ट्रेस नहीं होंगे।

यदि आप करते हैं throw, तो अपवाद केवल रेखा से नीचे चला जाता है और आपको पूर्ण स्टैक ट्रेस मिल जाएगा।


4

नहीं, यह एक अलग स्टैक ट्रेस के अपवाद का कारण होगा। केवल हैंडलर throwमें बिना किसी अपवाद वस्तु का उपयोग catchकरने से स्टैक ट्रेस अपरिवर्तित हो जाएगा।

आप HandleException से एक बूलियन वापस करना चाह सकते हैं कि क्या अपवाद पुन: डूब जाएगा या नहीं।


4

MSDN के लिए खड़ा है :

एक बार एक अपवाद को फेंक दिया जाता है, जो जानकारी होती है उसका एक हिस्सा स्टैक ट्रेस होता है। स्टैक ट्रेस एक विधि कॉल पदानुक्रम की सूची है जो उस पद्धति से शुरू होती है जो अपवाद को फेंकता है और उस विधि से समाप्त होता है जो अपवाद को पकड़ता है। यदि थ्रो स्टेटमेंट में अपवाद निर्दिष्ट करके एक अपवाद को फिर से फेंक दिया जाता है, तो स्टैक ट्रेस को वर्तमान विधि पर फिर से शुरू किया जाता है और मूल विधि के बीच विधि कॉल की सूची अपवाद को फेंक देती है और वर्तमान विधि खो जाती है। अपवाद के साथ मूल स्टैक ट्रेस जानकारी रखने के लिए, अपवाद निर्दिष्ट किए बिना थ्रो स्टेटमेंट का उपयोग करें।


2

यहाँ देखो: http://blog-mstechnology.blogspot.de/2010/06/throw-vs-thod-we.html

फेंको :

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

यह अपवाद के साथ स्टैक जानकारी को संरक्षित करता है

इसे "रेथ्रो" कहा जाता है

यदि नया अपवाद फेंकना चाहते हैं,

throw new ApplicationException("operation failed!");

थ्रो एक्स :

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

यह अपवाद के साथ स्टैक जानकारी नहीं भेजेगा

इसे "ब्रेकिंग द स्टैक" कहा जाता है

यदि नया अपवाद फेंकना चाहते हैं,

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

0

यदि आप एक क्लाइंट को एपीआई प्रदान कर रहे हैं और आप अपने आंतरिक पुस्तकालय के लिए वर्बोज़ स्टैक ट्रेस जानकारी प्रदान करना चाहते हैं, तो इस पर आपको एक अलग दृष्टिकोण देने के लिए थ्रो का उपयोग करना विशेष रूप से उपयोगी है। यहाँ फेंक का उपयोग करके, मैं File.Delete के लिए System.IO.File पुस्तकालय के इस मामले में स्टैक ट्रेस मिल जाएगा। अगर मैं थ्रो एक्स का इस्तेमाल करता हूं, तो वह जानकारी मेरे हैंडलर को नहीं दी जाएगी।

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. यदि सभी पंक्ति 1, 2 और 3 में टिप्पणी की गई है - आउटपुट - आंतरिक पूर्व

  2. यदि सभी लाइन 2 और 3 पर टिप्पणी की गई है - आउटपुट - इनर एक्स सिस्टम। डीवेडबायजेरोएक्स अपवाद: {"शून्य से विभाजित करने का प्रयास किया गया।"}} ---------

  3. अगर सभी लाइन 1 और 2 पर टिप्पणी की जाती है - आउटपुट - इनर एक्स सिस्टम। अपवाद: 0 से विभाजित करें ----

  4. यदि सभी लाइन 1 और 3 पर टिप्पणी की जाती है - आउटपुट - इनर एक्स सिस्टम। डीविडेबायजेरोएक्स अपवाद: {"शून्य से विभाजित करने का प्रयास किया गया।"}} ---------

और StackTrace थ्रो एक्स के मामले में रीसेट हो जाएगा;

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.