रयान डेविस की रूबी क्विकर कहती है (बिना स्पष्टीकरण के):
अपवाद बचाव मत करो। कभी। या मैं तुम्हें चाकू मार दूंगा।
क्यों नहीं? सही बात क्या है?
रयान डेविस की रूबी क्विकर कहती है (बिना स्पष्टीकरण के):
अपवाद बचाव मत करो। कभी। या मैं तुम्हें चाकू मार दूंगा।
क्यों नहीं? सही बात क्या है?
जवाबों:
टीएल; डीआर : StandardError
सामान्य अपवाद पकड़ने के बजाय उपयोग करें । जब मूल अपवाद फिर से उठाया जाता है (उदाहरण के लिए केवल अपवाद को लॉग करने के लिए बचाया Exception
जाता है ), तो शायद बचाव ठीक है।
Exception
की जड़ है रूबी के अपवाद पदानुक्रम , इसलिए जब आप rescue Exception
आप से बचाव सब कुछ , जैसे उपवर्गों सहित SyntaxError
, LoadError
, और Interrupt
।
बचाव कार्य Interrupt
उपयोगकर्ता CTRLCको प्रोग्राम से बाहर निकलने से रोकता है ।
रेस्क्यूइंग SignalException
प्रोग्राम को संकेतों को सही ढंग से जवाब देने से रोकता है। इसे छोड़कर यह अकल्पनीय होगा kill -9
।
बचाव का SyntaxError
मतलब है eval
कि असफलता चुपचाप ऐसा करेगी।
इन सभी को इस कार्यक्रम को चलाकर, CTRLCया kill
इसे करने की कोशिश करके दिखाया जा सकता है:
loop do
begin
sleep 1
eval "djsakru3924r9eiuorwju3498 += 5u84fior8u8t4ruyf8ihiure"
rescue Exception
puts "I refuse to fail or be stopped!"
end
end
इससे बचना Exception
भी डिफ़ॉल्ट नहीं है। करते हुए
begin
# iceberg!
rescue
# lifeboats
end
से बचाव नहीं है Exception
, यह से बचाता है StandardError
। आपको आम तौर पर डिफ़ॉल्ट की तुलना में कुछ अधिक विशिष्ट निर्दिष्ट करना चाहिए StandardError
, लेकिन इसे संकीर्ण करने के बजाय गुंजाइश को Exception
व्यापक बनाने से बचाया जा सकता है, और विनाशकारी परिणाम हो सकते हैं और बग-शिकार को बेहद मुश्किल बना सकते हैं।
यदि आपके पास ऐसी स्थिति है जहां आप बचाव करना चाहते हैं StandardError
और आपको अपवाद के साथ एक चर की आवश्यकता है, तो आप इस फॉर्म का उपयोग कर सकते हैं:
begin
# iceberg!
rescue => e
# lifeboats
end
जो इसके बराबर है:
begin
# iceberg!
rescue StandardError => e
# lifeboats
end
कुछ सामान्य मामलों में से एक जहां से बचाव के Exception
लिए यह लॉगिंग / रिपोर्टिंग उद्देश्यों के लिए है, जिस स्थिति में आपको तुरंत अपवाद को फिर से उठाना चाहिए:
begin
# iceberg?
rescue Exception => e
# do some logging
raise # not enough lifeboats ;)
end
Throwable
जावा में पकड़ने की तरह है
ADAPTER_ERRORS = [::ActiveRecord::StatementInvalid, PGError, Mysql::Error, Mysql2::Error, ::ActiveRecord::JDBCError, SQLite3::Exception]
और फिरrescue *ADAPTER_ERRORS => e
वास्तविक नियम है: दूर अपवाद न फेंके। आपके उद्धरण के लेखक की निष्पक्षता संदिग्ध है, जैसा कि इस तथ्य से स्पष्ट है कि यह समाप्त होता है
या मैं तुम्हें चाकू मार दूंगा
बेशक, इस बात से अवगत रहें कि सिग्नल (डिफ़ॉल्ट रूप से) अपवादों को फेंकते हैं, और आम तौर पर लंबे समय तक चलने वाली प्रक्रियाओं को सिग्नल के माध्यम से समाप्त किया जाता है, इसलिए अपवाद को पकड़ना और सिग्नल अपवादों को समाप्त नहीं करना आपके कार्यक्रम को रोकना बहुत कठिन बना देगा। तो यह मत करो:
#! /usr/bin/ruby
while true do
begin
line = STDIN.gets
# heavy processing
rescue Exception => e
puts "caught exception #{e}! ohnoes!"
end
end
नहीं, वास्तव में, यह मत करो। यह देखने के लिए कि क्या यह काम करता है, भी न चलाएं।
हालाँकि, मान लें कि आपके पास एक थ्रेडेड सर्वर है और आप चाहते हैं कि सभी अपवाद न हों:
thread.abort_on_exception = true
)। फिर यह आपके कनेक्शन हैंडलिंग थ्रेड में पूरी तरह से स्वीकार्य है:
begin
# do stuff
rescue Exception => e
myLogger.error("uncaught #{e} exception while handling connection: #{e.message}")
myLogger.error("Stack trace: #{backtrace.map {|l| " #{l}\n"}.join}")
end
ऊपर रूबी के डिफ़ॉल्ट अपवाद हैंडलर की भिन्नता के लिए काम करता है, इस लाभ के साथ कि यह आपके प्रोग्राम को भी नहीं मारता है। रेल्स अपने अनुरोध हैंडलर में ऐसा करती हैं।
सिग्नल अपवाद मुख्य धागे में उठाए गए हैं। बैकग्राउंड थ्रेड्स उन्हें नहीं मिलेंगे, इसलिए उन्हें वहां पकड़ने की कोशिश करने का कोई मतलब नहीं है।
यह उत्पादन वातावरण में विशेष रूप से उपयोगी है, जहाँ आप नहीं चाहते हैं कि जब भी कुछ गलत हो , तो आप अपना कार्यक्रम बस रोक दें। तब आप अपने लॉग में स्टैक डंप ले सकते हैं और कॉल श्रृंखला के नीचे और अधिक सुंदर तरीके से विशिष्ट अपवाद से निपटने के लिए अपने कोड में जोड़ सकते हैं।
ध्यान दें कि एक और रूबी मुहावरा है जिसका प्रभाव बहुत अधिक है:
a = do_something rescue "something else"
इस पंक्ति में, यदि do_something
कोई अपवाद उठाता है , तो उसे रूबी द्वारा पकड़ लिया जाता है, फेंक दिया जाता है और a
उसे सौंपा जाता है "something else"
।
आम तौर पर, ऐसा मत करो, विशेष मामलों को छोड़कर जहां आप जानते हैं कि आपको चिंता करने की आवश्यकता नहीं है। एक उदाहरण:
debugger rescue nil
debugger
समारोह अपने कोड में एक ब्रेकपाइंट सेट करने के लिए एक नहीं बल्कि अच्छा तरीका है, लेकिन अगर रेल एक डिबगर के बाहर चल रहा है, और, यह एक अपवाद को जन्म देती है। अब सैद्धांतिक रूप से आपको डिबग कोड को अपने कार्यक्रम में नहीं छोड़ना चाहिए (pff! कोई भी ऐसा नहीं करता है!) लेकिन आप इसे किसी कारण से थोड़ी देर के लिए वहीं रखना चाह सकते हैं, लेकिन लगातार अपने डिबगर को न चलाएं।
ध्यान दें:
यदि आपने किसी और के प्रोग्राम को चलाया है जो सिग्नल अपवादों को पकड़ता है और उन्हें अनदेखा करता है, (ऊपर दिए गए कोड को कहें) तो:
pgrep ruby
, या ps | grep ruby
, अपने अपमानजनक प्रोग्राम के पीआईडी के लिए देखें, और फिर चलाएं kill -9 <PID>
। यदि आप किसी और के कार्यक्रम के साथ काम कर रहे हैं, जो भी किसी भी कारण से, इन उपेक्षा-अपवाद ब्लॉकों के साथ peppered है, तो इसे मेनलाइन के शीर्ष पर रखना एक संभव पुलिस-आउट है:
%W/INT QUIT TERM/.each { |sig| trap sig,"SYSTEM_DEFAULT" }
यह प्रोग्राम को तुरंत समाप्त करने के लिए सामान्य समाप्ति संकेतों का जवाब देने का कारण बनता है, अपवाद हैंडलर को दरकिनार करते हुए, जिसमें कोई सफाई नहीं है । तो यह डेटा हानि या समान हो सकता है। सावधान रहे!
यदि आपको ऐसा करने की आवश्यकता है:
begin
do_something
rescue Exception => e
critical_cleanup
raise
end
आप वास्तव में ऐसा कर सकते हैं:
begin
do_something
ensure
critical_cleanup
end
दूसरे मामले में, critical cleanup
हर बार बुलाया जाएगा, चाहे एक अपवाद फेंका जाए या नहीं।
kill -9
।
ensure
चाहे कोई अपवाद उठाया गया हो या नहीं, फिर भी rescue
चलेगा , जबकि अपवाद केवल तभी उठाया जाएगा जब कोई अपवाद उठाया गया था।
मत करो rescue Exception => e
(अपवाद नहीं है और फिर से बढ़ा) - या आप कर सकते हैं एक पुल से ड्राइव।
मान लीजिए कि आप एक कार में हैं (रूबी चल रहे हैं)। आपने हाल ही में ओवर-द-एयर अपग्रेड सिस्टम (जो उपयोग करता है eval
) के साथ एक नया स्टीयरिंग व्हील स्थापित किया है , लेकिन आपको पता नहीं है कि प्रोग्रामर में से एक सिंटैक्स पर गड़बड़ कर दिया है।
आप एक पुल पर हैं, और महसूस करते हैं कि आप रेलिंग की ओर जा रहे हैं, इसलिए आप बाएं मुड़ते हैं।
def turn_left
self.turn left:
end
उफ़! यह शायद अच्छा नहीं है ™, सौभाग्य से, रूबी एक उठाती है SyntaxError
।
कार को तुरंत रोकना चाहिए - सही है?
नहीं।
begin
#...
eval self.steering_wheel
#...
rescue Exception => e
self.beep
self.log "Caught #{e}.", :warn
self.log "Logged Error - Continuing Process.", :info
end
बीप बीप
चेतावनी: पकड़ा गया वाक्यविन्यास अपवाद।
जानकारी: त्रुटि लॉग - प्रक्रिया जारी है।
आप कुछ गलत है नोटिस, और आप आपातकालीन ब्रेक पर स्लैम ( ^C
: Interrupt
)
बीप बीप
चेतावनी: पकड़ा गया व्यवधान।
जानकारी: त्रुटि लॉग - प्रक्रिया जारी है।
हाँ - इससे बहुत मदद नहीं मिली। आप रेल के बहुत पास हैं, इसलिए आप कार को पार्क में रखें ( kill
आईएनजी:) SignalException
।
बीप बीप
चेतावनी: सिग्नल एक्सेप्शन अपवाद पकड़ा गया।
जानकारी: त्रुटि लॉग - प्रक्रिया जारी है।
अंतिम सेकंड में, आप कुंजियाँ खींचते हैं ( kill -9
), और कार रुक जाती है, आप स्टीयरिंग व्हील में आगे की ओर खिसक जाते हैं (एयरबैग को फुलाया नहीं जा सकता क्योंकि आपने प्रोग्राम को शालीनतापूर्वक रोका नहीं था - आपने इसे समाप्त कर दिया), और कंप्यूटर अपनी कार के पिछले हिस्से में सामने की ओर खिसक लें। कोक का आधा भरा हुआ कागज़ कागजों पर फैल सकता है। पीठ में किराने का सामान कुचल दिया जाता है, और अधिकांश अंडे की जर्दी और दूध में ढंके होते हैं। कार को गंभीर मरम्मत और सफाई की आवश्यकता है। (डेटा हानि)
उम्मीद है कि आपके पास बीमा (बैकअप) है। अरे हाँ - क्योंकि एयरबैग फुलाया नहीं गया, आपको शायद चोट लगी है (निकाल दिया जा रहा है, आदि)।
लेकिन रुकें! वहाँ हैअधिकआप क्यों उपयोग करना चाहते हैं कारण rescue Exception => e
!
मान लीजिए कि आप वह कार हैं, और आप यह सुनिश्चित करना चाहते हैं कि अगर कार अपनी सुरक्षित गति को पार कर रही है तो एयरबैग फुलाए।
begin
# do driving stuff
rescue Exception => e
self.airbags.inflate if self.exceeding_safe_stopping_momentum?
raise
end
यहां नियम का अपवाद है: आप Exception
केवल तभी पकड़ सकते हैं जब आप अपवाद को फिर से बढ़ाएं । इसलिए, एक बेहतर नियम यह है कि कभी न निगलें Exception
, और हमेशा त्रुटि को फिर से उठाएं।
लेकिन बचाव को जोड़ना रूबी जैसी भाषा में भूलना आसान है, और किसी मुद्दे को फिर से उठाने से ठीक पहले एक बचाव वक्तव्य देना थोड़ा गैर-डीआरवाई लगता है। और आप कथन को भूलना नहीं चाहते raise
। और यदि आप करते हैं, तो सौभाग्य उस त्रुटि को खोजने की कोशिश कर रहा है।
शुक्र है, रूबी कमाल की है, आप बस ensure
कीवर्ड का उपयोग कर सकते हैं , जो यह सुनिश्चित करता है कि कोड चलता है। ensure
कीवर्ड कोई बात नहीं क्या कोड चलेंगे - अगर एक अपवाद फेंक दिया जाता है, अगर एक नहीं है, एकमात्र अपवाद अगर किया जा रहा है दुनिया समाप्त होता है (या अन्य संभावना नहीं घटनाओं)।
begin
# do driving stuff
ensure
self.airbags.inflate if self.exceeding_safe_stopping_momentum?
end
बूम! और उस कोड को वैसे भी चलाना चाहिए। एकमात्र कारण जो आपको उपयोग करना चाहिए rescue Exception => e
, यदि आपको अपवाद तक पहुंच की आवश्यकता है, या यदि आप केवल एक अपवाद पर कोड चलाना चाहते हैं। और त्रुटि को फिर से उठाना याद रखें। हर बार।
नोट: जैसा कि @Niall ने बताया है, हमेशा रन सुनिश्चित करें । यह अच्छा है क्योंकि कभी-कभी आपका कार्यक्रम आपसे झूठ बोल सकता है और अपवादों को नहीं फेंक सकता, तब भी जब मुद्दे होते हैं। महत्वपूर्ण कार्यों के साथ, एयरबैग को फुलाए जाने की तरह, आपको यह सुनिश्चित करने की आवश्यकता है कि ऐसा कुछ भी न हो। इस वजह से, हर बार जाँच करना बंद कर दिया जाता है कि कार एक अपवाद है या नहीं, एक अच्छा विचार है। भले ही एयरबैग को फुलाया जाना अधिकांश प्रोग्रामिंग संदर्भों में एक असामान्य काम है, लेकिन यह वास्तव में अधिकांश सफाई कार्यों के साथ बहुत आम है।
ensure
एक विकल्प के रूप में के बारे में rescue Exception
भ्रामक है - उदाहरण का अर्थ है कि वे समतुल्य हैं, लेकिन जैसा कि कहा गया ensure
है कि एक अपवाद है या नहीं, इसलिए अब आपके एयरबैग फुलाएंगे क्योंकि आप 5mph से अधिक चले गए, हालांकि कुछ भी गलत नहीं हुआ।
क्योंकि यह सभी अपवादों को पकड़ लेता है। यह संभावना नहीं है कि आपका कार्यक्रम उनमें से किसी से भी पुनर्प्राप्त कर सकता है।
आपको केवल उन अपवादों को संभालना चाहिए जिन्हें आप जानते हैं कि इससे कैसे उबरना है। यदि आप एक विशेष प्रकार के अपवाद का अनुमान नहीं लगाते हैं, तो इसे संभालें नहीं, ज़ोर से क्रैश करें (लॉग को विवरण लिखें), फिर लॉग और फिक्स कोड का निदान करें।
अपवादों को निगलना बुरा है, ऐसा मत करो।
यह नियम का एक विशिष्ट मामला है कि आपको किसी भी अपवाद को नहीं पकड़ना चाहिए जिसे आप नहीं जानते कि कैसे संभालना है। यदि आप नहीं जानते कि इसे कैसे संभालना है, तो सिस्टम के कुछ अन्य भाग को पकड़ना और उसे संभालना बेहतर होता है।
मैं सिर्फ हनीबैगर पर इसके बारे में एक महान ब्लॉग पोस्ट पढ़ता हूं ।
आपको अपवाद क्यों नहीं करना चाहिए
एक्सेप्शन को रेस्क्यू करने में समस्या यह है कि यह वास्तव में एक्सेप्शन से निकलने वाले हर अपवाद को बचाता है। जो .... वो सब!
यह एक समस्या है क्योंकि कुछ अपवाद हैं जो रूबी द्वारा आंतरिक रूप से उपयोग किए जाते हैं। उन्हें आपके ऐप से कोई लेना-देना नहीं है, और उन्हें निगलने से बुरा काम होगा।
यहाँ कुछ बड़े हैं:
SignalException :: Interrupt - यदि आप इसे बचाव करते हैं, तो आप नियंत्रण-सी मारकर अपने ऐप से बाहर नहीं निकल सकते हैं।
ScriptError :: SyntaxError - वाक्यविन्यास त्रुटियों को निगलने का अर्थ है कि चीजें ("कुछ भूल गई") चुपचाप विफल हो जाएंगी।
NoMemoryError - पता है कि क्या होता है जब आपका प्रोग्राम सभी RAM का उपयोग करने के बाद भी चलता रहता है? न ही मैं।
begin do_something() rescue Exception => e # Don't do this. This will swallow every single exception. Nothing gets past it. end
मैं अनुमान लगा रहा हूं कि आप वास्तव में इनमें से किसी भी सिस्टम-स्तर के अपवादों को निगलना नहीं चाहते हैं। आप केवल अपने सभी एप्लिकेशन स्तर की त्रुटियों को पकड़ना चाहते हैं। अपवाद आपके कोड का कारण बने।
सौभाग्य से, इसका एक आसान तरीका है।