เริ่มต้นช่วยเหลือและมั่นใจใน Ruby?


547

ฉันเพิ่งเริ่มเขียนโปรแกรมใน Ruby และฉันกำลังดูการจัดการข้อยกเว้น

ฉันสงสัยว่าensureทับทิมนั้นเทียบเท่ากับfinallyใน C # หรือไม่ ฉันควรจะมี:

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
ไม่ดี ตามกฎแล้วเมื่อจัดการกับทรัพยากรภายนอกคุณเสมอต้องการ opening` ทรัพยากรที่จะเป็นภายในbeginบล็อก
Nowaker

คำตอบ:


1181

ใช่ensureรับรองว่าจะมีการประเมินรหัสอยู่เสมอ ensureนั่นเป็นเหตุผลที่มันเรียกว่า ดังนั้นจึงจะเทียบเท่ากับ C # ของ Java และ finally's

โฟลว์ทั่วไปของ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คุณยังสามารถละทิ้งตัวแปรซึ่งในกรณีนี้คุณจะไม่สามารถตรวจสอบข้อยกเว้นในโค้ดการจัดการข้อยกเว้นของคุณ (ดีคุณสามารถใช้ตัวแปรยกเว้นทั่วโลกในการเข้าถึงข้อยกเว้นสุดท้ายที่ถูกยกขึ้น แต่ที่ hacky นิด ๆ หน่อย ๆ .) และคุณสามารถออกจากชั้นยกเว้นในกรณีที่มีข้อยกเว้นทุกสิ่งที่สืบทอดจาก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บล็อกใน C # ยกเว้นว่า Ruby มีประสิทธิภาพเพียงพอที่คุณไม่ต้องรอให้มหาปุโรหิตของ Microsoft ลงมาจากภูเขาและเปลี่ยนคอมไพเลอร์ให้คุณ ใน Ruby คุณสามารถสร้างมันเองได้:

# 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แล้ว แต่มันเป็นรูปแบบทั่วไปที่คุณสามารถใช้ในรหัสของคุณเองเช่นกันสำหรับการใช้งานการล้างทรัพยากร (เช่น la 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คำสั่งจะดำเนินการครั้งสุดท้ายพวกเขาไม่ได้เป็นค่าตอบแทน
Chris

30
ฉันชอบที่จะเห็นการมีส่วนร่วมเช่นนี้ใน SO มันเหนือกว่าสิ่งที่ OP ขอเช่นนั้นนำไปใช้กับนักพัฒนาจำนวนมาก แต่ยังอยู่ในหัวข้อ ฉันเรียนรู้เล็ก ๆ น้อย ๆ จากคำตอบ + การแก้ไขนี้ ขอบคุณที่ไม่ใช่แค่เขียนว่า "ใช่ensureถูกเรียกมาไม่ว่าอะไรก็ตาม"
เดนนิส

3
โปรดทราบว่าไม่รับประกันว่าจะสมบูรณ์ ใช้กรณีที่คุณมีการเริ่มต้น / มั่นใจ / สิ้นสุดภายในเธรดแล้วคุณเรียก Thread.kill เมื่อบรรทัดแรกของบล็อกการให้แน่ใจว่าจะถูกเรียก สิ่งนี้จะทำให้ส่วนที่เหลือของการรับประกันไม่ดำเนินการ
ตุ๊กตา

5
@ เท็ดดี้: รับประกันว่าจะเริ่มดำเนินการไม่รับประกันว่าจะเสร็จสมบูรณ์ ตัวอย่างของคุณคือ overkill - ข้อยกเว้นง่ายๆภายในบล็อกการตรวจสอบจะทำให้ออกจากเช่นกัน
Martin Konecny

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
ฉันเดาว่าถ้าคุณไม่ต้องการจัดการข้อผิดพลาด แต่เพียงแค่ยกมันขึ้นมาและปิดตัวจัดการไฟล์คุณไม่ต้องการการช่วยเหลือเริ่มต้นที่นี่
rogerdpack


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 สิ่งนี้มีประโยชน์มากสำหรับการทำให้แน่ใจว่าทรัพยากรที่สำคัญได้รับการคุ้มครองเช่นการปิดตัวจัดการไฟล์โดยผิดพลาดหรือการปล่อย mutex


ยกเว้นในกรณีของเขา / เธอไม่มีการรับประกันสำหรับไฟล์ที่จะปิดเพราะFile.openส่วนหนึ่งไม่ได้อยู่ในบล็อกเริ่มต้นให้แน่ใจว่า มีเพียงfile.closeแต่มันไม่เพียงพอ
Nowaker
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.