2011-09-15 4 views
15

नेट फ्रेमवर्क प्रयास-पकड़ कार्यान्वयन केवल आपको उन वर्गों को पकड़ने की अनुमति देता है जो बेस क्लास "सिस्टम.एक्सप्शन" से वंचित हैं। यह "सिस्टम.इसेप्शन" जैसे इंटरफ़ेस क्यों नहीं हो सकता था?क्यों .NET अपवाद बेस क्लास के बजाय इंटरफ़ेस के विरुद्ध काम नहीं करते हैं?

मामला उपयोग

हम एक एपीआई कि System.Exception बंद विरासत में एक कस्टम आधार वर्ग का उपयोग करें। यह केवल एक बार एक अपवाद लॉग किया गया है फेंक दिया जाता है और इसलिए हम आसानी से की तरह कुछ के द्वारा फिर से प्रवेश करने से बच सकते हैं:

try 
{ 
    // Do something. 
} 
catch (LoggedException) 
{ 
    // Already logged so just rethrow. 
    throw; 
} 
catch (Exception ex) 
{ 
    // TODO: Log exception. 
    throw new LoggedException("Failed doing something.", ex); 
} 

यह महान जब तक आप कि इस तरह के सिस्टम के रूप में एक और सिस्टम अपवाद प्रकार बंद विरासत में एक कस्टम अपवाद चाहते हैं। FormatException

इन दोनों को संभालने का एकमात्र तरीका दो कस्टम बेस प्रकारों और प्रत्येक पकड़ विवरण को डुप्लिकेट करना है।

पुनर्रचना

.नेट फ्रेमवर्क बस ऐसे System.IException के रूप में कुछ के लिए देखा तो आप बस ऐसे CompanyName.ILoggedException रूप में एक कस्टम अपवाद इंटरफेस हो सकता है, System.IException इनहेरिट जो अपने सभी कस्टम अपवाद प्रकार लागू । इसलिए आपका नया कैच कोड कुछ ऐसा दिखाई देगा:

try 
{ 
    // Do something. 
} 
catch (ILoggedException) 
{ 
    // Already logged so just rethrow. 
    throw; 
} 
catch (IException ex) 
{ 
    // TODO: Log exception. 
    throw new CustomException("Failed doing something.", ex); 
} 

क्या कोई व्यावहारिक कारण इस तरह से ढांचा लागू किया गया है? या यह नेट फ्रेमवर्क के भविष्य के संस्करण में अनुरोध करने के लिए कुछ होगा?

+0

तुम्हें पता है कि आप बस ('पकड़ में' is' ऑपरेटर इस्तेमाल कर सकते हैं अपवाद पूर्व) ', है ना? 'अगर (ex ilgedgedException है)' – xanatos

+0

@xanatos: लेकिन यह सुविधा – sll

+0

@xanatos हाँ में निर्मित के बजाय एक हैक/काम है, इसके बारे में भी पोस्ट करने के बाद ही, लेकिन मैं व्यक्तिगत रूप से इस तरह कुछ देखना पसंद करूंगा भविष्य में रिलीज में ... शायद हम इसे कर सकते हैं! – danielrbradley

उत्तर

5

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

विस्तार के तरीकों की आवश्यकता:

private const string ExceptionLoggedKey = "IsExceptionLogged"; 

public static bool IsLogged(this Exception exception) 
{ 
    if (exception.Data.Contains(ExceptionLoggedKey)) 
    { 
     return (bool)exception.Data[ExceptionLoggedKey]; 
    } 
    return false; 
} 

public static void SetLogged(this Exception exception) 
{ 
    exception.Data.Add(ExceptionLoggedKey, true); 
} 

कंपनी अपवाद निम्न स्वरूप ले, निर्माता में IsLogged ध्वज की स्थापना:

public class CompanysLoggedException : InvalidOperationException //Could be any Exception 
{ 
    public CompanysLoggedException() 
    { 
     this.SetLogged(); 
    } 
} 

ट्राई/कैच उपयोग:

try 
{ 
    throw new ArgumentException("There aren't any arguments."); 
} 
catch (Exception ex) 
{ 
    if (ex.IsLogged()) 
     //Nothing additional to do - simply throw the exception 
     throw; 
    else 
     //TODO Log the exception 
     throw new CompanysLoggedException(); 
} 

मैं इस बात से सहमत नहीं है कि यह निश्चित रूप से अपवाद से मेल खाने की क्षमता के रूप में साफ नहीं है एक कार्यान्वित इंटरफ़ेस पर, लेकिन मुझे लगता है कि पैटर्न काफी संक्षिप्त और पठनीय है। परिभाषित प्रत्येक नई कंपनी अपवाद के लिए SetLogged() को कॉल जोड़ने के लिए याद रखना हालांकि एक दोष है।

+0

बहुत उपयोगी, मैं कल ऐसा करने के लिए डेटा संग्रह को देख रहा था, और विधि की वैधता की जांच करने के लिए एक नया प्रश्न तैयार कर रहा था। मैं आसान पहुंच देने के लिए विस्तार विधि के अतिरिक्त की तरह करता हूं। एकमात्र अन्य जोड़ा जो मैं सोच रहा था, कुंजी के लिए सिंगलटन ऑब्जेक्ट का उपयोग करना था क्योंकि यह एक ऑब्जेक्ट -> ऑब्जेक्ट संग्रह है जो स्ट्रिंग के बजाए किसी अन्य कुंजी के साथ किसी भी संभावित विवाद से बचने के लिए होता है। – danielrbradley

+0

स्ट्रिंग कुंजी निकालने के लिए प्रतिक्रिया किए गए उत्तर - यह मान एक्सटेंशन विधि स्थैतिक वर्ग के साथ encapsulated किया जाना चाहिए, क्योंकि इसे इस संदर्भ के बाहर एक्सेस किया जाना चाहिए, वहां निजी निजी कॉन्स – Alex

+0

"कैच" का उपयोग करके उपरोक्त की तरह कुछ लिखने के बारे में कैसे करें। .. जब ", और इसे एक विधि-रैपर डीएलएल के रूप में कोडिंग करते हैं, ताकि सी # के अपवाद फ़िल्टर की कमी हो सके? – supercat

14

कि आप जानते ही है कि आधार वर्ग मामले में हम केवल एक भाग है, लेकिन इंटरफ़ेस के मामले में एक वर्ग के कई इंटरफेस को लागू कर सकते, इसलिए यदि आप की तरह कुछ है:

class MyException : IExceptionB, IExceptionA 
{ 
} 

try 
{ 
throw new MyException(); 
} 
catch(IExceptionB b){} 
catch(IExceptionA b){} 

अब इस बारे में कैसे करने के लिए अस्पष्टता बनाता है तय करें कि कौन सा कैच हैंडलर कॉल अप करने के लिए अपवाद के रूप में दोनों इंटरफेस लागू करता है यानी पदानुक्रम के मामले में दोनों बेस क्लास के विपरीत समान स्तर पर हैं जहां एक ही स्तर पर दो वर्ग नहीं होंगे।

कोड एक अपवाद अपने कोड दे के सिर्फ एक तरीका है की तुलना में अधिक है कि कुछ गलत हो गया है वह यह है कि मामले इंटरफ़ेस आधारित पकड़ में समस्या दिखा

+6

और यदि आप दोनों कैच डालते हैं तो आप ArgumentNullException और ArgumentException के बीच अंतर कैसे हल करते हैं? यह वही समस्या है। (दूसरा व्यक्ति पहली बार विरासत में आता है)। 'पकड़ने' का क्रम उनकी सापेक्ष प्राथमिकता को परिभाषित करता है। – xanatos

+2

हालांकि, यह माता-पिता और बाल अपवाद प्रकारों के साथ हो सकता है। इस मामले में ढांचा पहले पकड़ कथन का उपयोग करता है, लेकिन फिर दूसरा कैच स्टेटमेंट निष्पादित करेगा यदि पहले एक प्रकार को फेंकता है जो दूसरे प्रकार से मेल खाता है। – danielrbradley

+0

@info_dev जिसे आपने रखा है। इसे स्वयं आज़माएं: http://www.ideone.com/ZzXz2 – xanatos

1

अनुमति दी गई थी काल्पनिक है। इसमें सभी प्रकार की डीबग जानकारी (स्टैक ट्रेस, लक्ष्य, आंतरिक अपवाद, हार्सल्ट, वाटसन बाल्टी आदि) शामिल हैं और इसे कुछ इकट्ठा किया जाना है। सबसे तार्किक और सरल समाधान बेस क्लास को इकट्ठा करना है।

+0

हां, इसलिए इंटरफ़ेस का डिफ़ॉल्ट कार्यान्वयन करने के लिए पुन: उपयोग करने के लिए उपयोगी है, लेकिन किसी इंटरफ़ेस को लागू करने वाले किसी भी व्यक्ति को सभी प्रासंगिक कार्यों को कार्यान्वित करना है ताकि यह स्वयं को फिर से कार्यान्वित कर सके। – danielrbradley

+0

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

+0

@CPX: कोई एक ExceptionInfo ऑब्जेक्ट को काफी हद तक परिभाषित कर सकता है, और यह आवश्यक है कि IException टाइप ExceptionInfo प्रकार की जानकारी प्रॉपर्टी का खुलासा करे। – supercat

5

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

+0

+1 यह एक बहुत अच्छा तर्क है। मुझे नहीं पता कि यह वास्तव में एक समस्या होगी या नहीं। मुझे लगता है कि किसी ऑब्जेक्ट के कार्यान्वित कक्षाओं/इंटरफेस की सूची किसी प्रकार के शब्दकोश के समान ही सहेजी जाती है, इसलिए इसे एक्सेस करना केवल मूल सूची की जांच करने से थोड़ा महंगा है लेकिन अभी भी ... – xanatos

+0

+1 मेरी ओर देखो पद। सहमत हुए, लेकिन कमिन में, मुझे नहीं लगता कि संकलक में इसे कार्यान्वित करना मुश्किल था, मुझे भाषा के स्टैंडअर्ट पर इसे पेश करना मुश्किल था ("केवल इंटरफेस" कारण देखें) –

+0

दिलचस्प विचार, किसी को संदर्भ मिला अभिभावक वर्गों और इंटरफेस की प्रकार की जांच के बीच प्रदर्शन अंतर के लिए? – danielrbradley

1

यह वह जगह है लगभग आप का उल्लेख किया, कुछ "कोड चीनी" सोचा के बिना:

try 
{ 
} 
catch(LoggerException ex) 
{ 
    ex.WriteLog(); 
} 
catch(Exception ex) 
{ 
    ILoggerException1 l1 = ex as ILoggerException1; 
    if (l1 != null) 
    { 
     l1.WriteLog1(); 
    } 
    else 
    { 
     ILoggerException2 l2 = ex as ILoggerException2; 
     if (l2 != null) 
     { 
      l2.WriteLog2(); 
     } 
     else 
     { 
      ILoggerException3 l3 = ex as ILoggerException3; 
      if (l3 != null) 
      { 
       l3.WriteLog3(); 
      } 
      else 
      { 
       throw ex; 
      } 
     } 
    } 
} 

समर्थन के साथ संकलक फार्म यह लिखा जा whould के रूप में:

try 
{ 
} 
catch(LoggerException ex) 
{ 
    ex.WriteLog(); 
} 
// no more classes is allowed by the compiler be here, only interfaces in a tail catch recursion 
catch(ILoggerException1 ex1) 
{ 
    ex1.WriteLog(); 
} 
catch(ILoggerException2 ex2) 
{ 
    ex2.WriteLog(); 
} 
catch(ILoggerException3 ex3) 
{ 
    ex3.WriteLog(); 
} 
+0

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

2

यह संभव है VB में पकड़ने के लिए कुछ प्रकार का उपयोग करके इंटरफेस:

 
    ' Doesn't require external function, but will will require typecast 
    ' if it needs to actually use Ex as IMyExceptionInterface 
    Catch Ex As Exception When TypeOf(Ex) Is IMyExceptionInterface 
    ' Alternate form: requires pre-declared variable and a function: 
    ' Function TryCastIntoSecondParam(Of TSource As Class, TDest As Class) _ 
    '        (ByVal Thing As TSource, ByRef Dest As TDest) 
    ' Dim Result As TDest 
    ' Result = TryCast(Thing, TDest) 
    ' Dest = Result 
    ' Return Dest IsNot Nothing 
    ' End Function 
    Catch Ex As Exception When TryCastIntoSecondParam(Ex, myIMyException) 

यदि वीबी या सी # कंपाइलर कार्यान्वयन करना चाहते थे ऐसा करने के लिए, वे एक वाक्यविन्यास

 
    Catch Ex As IMyExceptionInterface ' vb 
    catch IExceptionInterface ex  ' C# 

का उपयोग करने और उपरोक्त कोड के साथ इसे लागू करने की अनुमति दे सकता है। कंपाइलर समर्थन के बिना भी, वीबी उपयोगकर्ता उपरोक्त कोड के साथ सही अर्थशास्त्र प्राप्त कर सकते हैं। सी # में, अपवाद को पकड़ना आवश्यक होगा, परीक्षण करें कि यह वांछित प्रकार है, और अगर यह नहीं है तो फिर से करें; पहली जगह में अपवाद को पकड़ने से बचने के लिए फ़िल्टर का उपयोग करने वाले लोगों के अर्थशास्त्र अलग-अलग हैं। ध्यान दें कि उपरोक्त संरचनाओं को लागू करने के लिए सी # कंपाइलर के लिए, इसे फ़िल्टर ब्लॉक का उपयोग करना होगा, लेकिन इसे प्रोग्रामर को फ़िल्टर ब्लॉक की सभी शक्तियों का खुलासा नहीं करना पड़ेगा - सी # कार्यान्वयनकर्ताओं ने जानबूझकर ऐसा करने से इंकार कर दिया है।

जो कुछ कहा जा रहा है, मुझे संदेह है कि मूल प्रश्न का उत्तर "डिजाइनर ऐसी चीज के लिए किसी भी अच्छे उपयोग के मामलों के बारे में सोच नहीं सकते", या हो सकता है कि "इंटरफेस को अधिक जटिल प्रकार के संकल्प की आवश्यकता हो कक्षाएं करते हैं, संभावना बनाते हैं कि अपवाद को पकड़ने का निर्णय लेने का केवल कार्य ही अपने अपवाद के साथ विफल हो सकता है। "

असल में, मुझे वर्ग के प्रकारों के उपयोग को नापसंद करने का एक तरीका के रूप में उपयोग करने के तरीके के रूप में नापसंद होता है, क्योंकि अपवाद को पकड़ना है या नहीं, इस सवाल के कारण अक्सर बड़े पैमाने पर ऑर्थोगोनल होता है कि इसका कारण क्या हुआ । यदि कोई दस्तावेज़ लोड करने का प्रयास विफल रहता है, तो मुझे इस सवाल में दिलचस्पी नहीं है कि क्या कुछ तर्क सीमा से बाहर था या कुछ सूचकांक सीमा से बाहर था, क्योंकि मैं इस सवाल में हूं कि क्या मैं प्रयास से सुरक्षित रूप से ठीक हो सकता हूं नाटक करके मैं इसे कभी नहीं बनाया था। वास्तव में क्या आवश्यक है अपवादों के लिए एक "गंभीरता" उपाय जिसे कॉल श्रृंखला को अपवाद बुलबुले के रूप में बढ़ाया या घटाया जा सकता है। ऐसी चीज vb.net में कुछ हद तक व्यावहारिक हो सकती है (जिसमें अपवाद फ़िल्टर हैं) लेकिन शायद सी # (जो नहीं) में नहीं है, लेकिन अंतर्निहित अपवादों के भीतर किसी भी समर्थन की कमी के कारण किसी भी मामले में सीमित होगा।

संपादित करें/परिशिष्ट

यह अगर एक एक DLL vb में लिखा का उपयोग करता है एक कोशिश/फिल्टर/पकड़/अंत में आवरण जो कुछ प्रतिनिधियों कॉल लागू करने के लिए एक सी # परियोजना के भीतर अपवाद फिल्टर का उपयोग करना संभव है। दुर्भाग्यवश, ऐसे डीएलएल का उपयोग करने के लिए रन-टाइम दक्षता और कोड की योग्यता के बीच कुछ ट्रेडऑफ की आवश्यकता होगी। मैंने मनमाने ढंग से इंटरफेस को पकड़ने के विशेष अनुकूलित उद्देश्य के लिए ऐसे डीएलएल को लागू करने के बारे में सोचा नहीं था; मुझे नहीं पता कि डीएलएल में ऐसी कार्यक्षमता को शामिल करने का कोई फायदा होगा या नहीं, यह जांचने के लिए कि यह एक अपवाद पकड़ा जाना चाहिए या नहीं, यह जांचने के लिए इसे लैम्ब्डा अभिव्यक्ति या अज्ञात विधि से गुजरना है।

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

+0

+1 यह एक बहुत साफ समाधान है। यह एक वास्तविक शर्म की बात है, यह सी # में समर्थित नहीं है, जो पहले नहीं हुआ था। इस विचार पर किसी और के लिए उपयोगी संदर्भ: http://blogs.msdn.com/b/jaredpar/archive/2008/10/09/vb-catch-when-why-so-special.aspx – danielrbradley

+0

@info_dev: देखें उपरोक्त परिशिष्ट – supercat

+0

मुझे लगता है कि अपवाद की गंभीरता को निर्धारित करने का मुद्दा ऐसा कुछ नहीं है जिसे आप वर्णन करने के तरीके में सामान्यीकृत कर सकते हैं (मान लीजिए कि मैं आपको सही तरीके से समझता हूं)। चाहे आप त्रुटि से पुनर्प्राप्त हो या नहीं, बहुत कार्यान्वयन निर्भर है। यदि आप फ़ाइल लोड करने में विफल रहते हैं क्योंकि उपयोगकर्ता उस पथ में प्रवेश करता है जो अस्तित्व में नहीं है, तो आप उसकी त्रुटि के उपयोगकर्ता को सूचित करके आसानी से इसे पुनर्प्राप्त कर सकते हैं। लेकिन अगर आप एक कॉन्फ़िगरेशन फ़ाइल लोड करने का प्रयास करते हैं जो मौजूद नहीं है, वसूली बहुत जटिल है। वही त्रुटि/अपवाद, लेकिन गंभीरता की पूरी तरह से अलग डिग्री। –

0

बदल जाता है कभी ढांचे 2.0 के बाद से एक अंतरफलक है बाहर:

System.Runtime.InteropServices._Exception 

इस इंटरफेस पर अधिक जानकारी के लिए जाना here

मैंने हालांकि एक वर्कअराउंड का उपयोग किया और एक अमूर्त वर्ग का उपयोग किया और कक्षा को लागू किया। मुझे गंदे लगते हैं लेकिन वास्तव में मुझे जो चाहिए था उसके लिए यह एक आसान कार्यान्वयन था।

+1

यह इंटरफ़ेस मौजूद है लेकिन अगर सिस्टम। अपवाद से इसका उत्तराधिकारी नहीं होता है तो कहीं भी किसी भी अपवाद वर्ग में कोई बहुलक नहीं है। मामला कौन सा है। – Prometheus

0

सी # 6 ने पेश किया exception filtering तो आप जो पूछते हैं वह अब सी # (it has long been possible in VB.Net) में संभव है। अब हम when कीवर्ड का उपयोग कर सकते हैं।

void Main() 
{ 
    try 
    { 
      ... 
    } 
    catch (Exception ex) when (ex is ILoggedException) 
    { 
     // Already logged so just rethrow. 
     throw; 
    } 
    catch (Exception ex) 
    { 
     // TODO: Log exception. 
     throw new Exception("Failed doing something.", ex); 
    } 
} 

क्लास और इंटरफ़ेस परिभाषाओं:

अपने कोड नए सिंटैक्स का उपयोग करने पुनर्संशोधित है

public interface ILoggedException { } 

public class CustomLoggedException : Exception, ILoggedException { ... }