रूबी में शुरू, बचाव और सुनिश्चित?


547

मैंने हाल ही में रूबी में प्रोग्रामिंग शुरू की है, और मैं अपवाद हैंडलिंग देख रहा हूं।

मैं सोच रहा था कि क्या ensureरूबी finallyC # के बराबर थी ? क्या मुझे होना चाहिए:

file = File.open("myFile.txt", "w")

begin
  file << "#{content} \n"
rescue
  #handle the error here
ensure
  file.close unless file.nil?
end

या मुझे यह करना चाहिए?

#store the file
file = File.open("myFile.txt", "w")

begin
  file << "#{content} \n"
  file.close
rescue
  #handle the error here
ensure
  file.close unless file.nil?
end

क्या ensureकोई फर्क नहीं पड़ता कहा जाता है, भले ही एक अपवाद नहीं उठाया है?


1
न ही अच्छा है। एक नियम के रूप में, जब बाहरी संसाधनों से निपटते हैं, तो आप हमेशा संसाधन को beginब्लॉक के अंदर खोलना चाहते हैं ।
Nowaker

जवाबों:


1181

हां, ensureयह सुनिश्चित करता है कि कोड का हमेशा मूल्यांकन किया जाए। इसलिए इसे कहा जाता है ensure। तो, यह जावा और C # के बराबर है finally

के सामान्य प्रवाह begin/ rescue/ else/ ensure/ endइस तरह दिखता है:

begin
  # something which might raise an exception
rescue SomeExceptionClass => some_variable
  # code that deals with some exception
rescue SomeOtherException => some_other_variable
  # code that deals with some other exception
else
  # code that runs only if *no* exception was raised
ensure
  # ensure that this code always runs, no matter what
  # does not change the final value of the block
end

आप बाहर छोड़ सकते हैं rescue, ensureया else। आप उस चर को भी छोड़ सकते हैं जिस स्थिति में आप अपने अपवाद हैंडलिंग कोड में अपवाद का निरीक्षण नहीं कर पाएंगे। (ठीक है, आप हमेशा उठाए गए अंतिम अपवाद तक पहुंचने के लिए वैश्विक अपवाद चर का उपयोग कर सकते हैं, लेकिन यह थोड़ा हैक है।) और आप अपवाद वर्ग को छोड़ सकते हैं, इस स्थिति में सभी अपवाद जिन्हें से विरासत में StandardErrorपकड़ा जाएगा। (कृपया ध्यान दें इसका मतलब यह है कि यह नहीं है कि सभी अपवाद फंस गए हैं, क्योंकि वहाँ अपवाद जिनमें से उदाहरण हैं कर रहे हैं Exception, लेकिन नहीं StandardError। ज्यादातर बहुत गंभीर अपवाद कि समझौता जैसे कार्यक्रम की अखंडता SystemStackError, NoMemoryError, SecurityError, NotImplementedError, LoadError, SyntaxError, ScriptError, Interrupt,SignalExceptionया SystemExit।)

कुछ ब्लॉक निहित अपवाद ब्लॉक बनाते हैं। उदाहरण के लिए, विधि की परिभाषाएं भी स्पष्ट रूप से अपवाद ब्लॉक हैं, इसलिए लिखने के बजाय

def foo
  begin
    # ...
  rescue
    # ...
  end
end

तुम बस लिखो

def foo
  # ...
rescue
  # ...
end

या

def foo
  # ...
ensure
  # ...
end

यही बात classपरिभाषाओं और moduleपरिभाषाओं पर लागू होती है ।

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

# This is what you want to do:
File.open('myFile.txt', 'w') do |file|
  file.puts content
end

# And this is how you might implement it:
def File.open(filename, mode='r', perm=nil, opt=nil)
  yield filehandle = new(filename, mode, perm, opt)
ensure
  filehandle&.close
end

और आप क्या जानते हैं: यह पहले से ही मुख्य पुस्तकालय में उपलब्ध है File.open। लेकिन यह एक सामान्य पैटर्न है जिसे आप अपने स्वयं के कोड में भी उपयोग कर सकते हैं, किसी भी तरह के रिसोर्स क्लीनअप ( usingC # में एक ला ) को लागू करने के लिए या लेनदेन या जो भी आप सोच सकते हैं।

एकमात्र मामला जहां यह काम नहीं करता है, अगर संसाधन प्राप्त करना और जारी करना कार्यक्रम के विभिन्न हिस्सों में वितरित किया जाता है। लेकिन अगर यह आपके उदाहरण के अनुसार स्थानीयकृत है, तो आप इन संसाधन ब्लॉकों का उपयोग आसानी से कर सकते हैं।


BTW: आधुनिक C # में, usingवास्तव में बहुत ही कम है, क्योंकि आप रूबी-शैली के संसाधन ब्लॉक को स्वयं लागू कर सकते हैं:

class File
{
    static T open<T>(string filename, string mode, Func<File, T> block)
    {
        var handle = new File(filename, mode);
        try
        {
            return block(handle);
        }
        finally
        {
            handle.Dispose();
        }
    }
}

// Usage:

File.open("myFile.txt", "w", (file) =>
{
    file.WriteLine(contents);
});

81
ध्यान दें कि, हालांकि ensureकथनों को अंतिम रूप से निष्पादित किया जाता है, वे रिटर्न मान नहीं हैं।
क्रिस

30
मुझे SO पर इस तरह समृद्ध योगदान देखकर बहुत अच्छा लगा। यह ऊपर और परे चला जाता है जो ओपी ने ऐसा पूछा था कि यह कई और डेवलपर्स पर लागू होता है, फिर भी विषय पर है। मैंने इस उत्तर + संपादन से कुछ चीजें सीखीं। सिर्फ "हां, ensureनहीं कहा जाता है कि क्या लिखा जाता है " लिखने के लिए धन्यवाद ।
डेनिस

3
ध्यान दें, यह सुनिश्चित करना पूर्ण होने की गारंटी नहीं है। एक थ्रेड के अंदर जहां आपके पास शुरुआत / सुनिश्चित / अंत है, और फिर आप थ्रेड.kill को कॉल करें, जब सुनिश्चित करें कि ब्लॉक की पहली पंक्ति को कॉल किया जा रहा है। यह बाकी सुनिश्चित करने के लिए निष्पादित नहीं करेगा।
टेडी

5
@ टेड्डी: सुनिश्चित करने के लिए निष्पादन शुरू करने की गारंटी है, पूरा करने की गारंटी नहीं है। आपका उदाहरण ओवरकिल है - सुनिश्चित ब्लॉक के अंदर एक सरल अपवाद यह भी बाहर निकलने का कारण होगा।
मार्टिन कोनसेनी

3
यह भी ध्यान दें कि कोई गारंटी नहीं है यह सुनिश्चित करना कहा जाता है। मैं गंभीर हूँ। पावर आउटेज / हार्डवेयर एरर / ओएस क्रैश हो सकता है, और यदि आपका सॉफ्टवेयर क्रिटिकल है, तो इस पर भी विचार करने की जरूरत है।
EdvardM

37

FYI करें, भले ही rescueअनुभाग में एक अपवाद फिर से उठाया गया ensureहो, कोड अपवाद निष्पादित करने से पहले अगले अपवाद हैंडलर पर ब्लॉक निष्पादित किया जाएगा। उदाहरण के लिए:

begin
  raise "Error!!"
rescue
  puts "test1"
  raise # Reraise exception
ensure
  puts "Ensure block"
end

14

यदि आप यह सुनिश्चित करना चाहते हैं कि कोई फ़ाइल बंद है तो आपको ब्लॉक फॉर्म का उपयोग करना चाहिए File.open:

File.open("myFile.txt", "w") do |file|
  begin
    file << "#{content} \n"
  rescue
  #handle the error here
  end
end

3
मुझे लगता है कि यदि आप त्रुटि को संभालना नहीं चाहते हैं, लेकिन इसे उठाएं, और फ़ाइल हैंडल को बंद करें, तो आपको यहां शुरुआती बचाव की आवश्यकता नहीं है?
रोज़गार पैक


5

हाँ, ensureयह हर बार चलाया जाता है, इसलिए आपको ब्लॉक file.closeमें ज़रूरत नहीं है begin

वैसे, परीक्षण करने का एक अच्छा तरीका है:

begin
  # Raise an error here
  raise "Error!!"
rescue
  #handle the error here
ensure
  p "=========inside ensure block"
end

आप यह देखने के लिए परीक्षण कर सकते हैं कि क्या "========= अंदर ब्लॉक सुनिश्चित करें" एक अपवाद होने पर मुद्रित किया जाएगा। फिर आप उस स्टेटमेंट पर टिप्पणी कर सकते हैं जो त्रुटि उठाता है और देखें कि क्या ensureस्टेटमेंट को यह देखने के द्वारा निष्पादित किया जाता है कि क्या कुछ भी प्रिंट आउट हो जाता है।


4

यही कारण है कि हमें इसकी आवश्यकता है ensure:

def hoge
  begin
    raise
  rescue  
    raise # raise again
  ensure  
    puts 'ensure' # will be executed
  end  
  puts 'end of func' # never be executed
end  

4

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


उसके मामले को छोड़कर, फ़ाइल बंद होने की कोई गारंटी नहीं है, क्योंकि File.openभाग शुरू-सुनिश्चित ब्लॉक के अंदर नहीं है। केवल file.closeहै, लेकिन यह पर्याप्त नहीं है।
नाऊकर
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.