Ruby'de Başlayın, Kurtarma ve Güvence Sağlama?


547

Yakın zamanda Ruby'de programlamaya başladım ve istisna yönetimine bakıyorum.

C # ensureRuby eşdeğer olup olmadığını merak ediyordum finally? Sahip olmalı mıyım:

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

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

yoksa bunu yapmalı mıyım?

#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

ensureBir istisna oluşmasa bile ne olursa olsun çağrılır mı ?


1
İkisi de iyi değil. Kural olarak, dış kaynaklarla uğraşırken , kaynak açılışının her zamanbegin blok içinde olmasını istersiniz .
Nowaker

Yanıtlar:


1181

Evet, ensurekodun her zaman değerlendirilmesini sağlar. Bu yüzden denir ensure. Yani, Java ve C # 'lara eşdeğerdir finally.

begin/ rescue/ else/ ensure/ Genel akışı endşu şekildedir:

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

Dışarıda bırakabilirsin rescue, ensureya else. Değişkenleri de dışarıda bırakabilirsiniz, bu durumda istisna işleme kodunuzdaki istisnayı inceleyemezsiniz. (Eh, yükseltilen son istisnaya erişmek için her zaman küresel istisna değişkenini kullanabilirsiniz, ancak bu biraz çirkin.) Ve istisna sınıfını dışarıda bırakabilirsiniz, bu durumda devralınan tüm istisnalar StandardErroryakalanır. (Not bu anlamına gelmez Lütfen bu tüm orada örnekleri olan istisnalar, çünkü istisnalar yakalanırlar Exceptionama StandardError. Çoğunlukla çok şiddetli istisnalar uzlaşmanın gibi programın bütünlüğü SystemStackError, NoMemoryError, SecurityError, NotImplementedError, LoadError, SyntaxError, ScriptError, Interrupt,SignalExceptionveya SystemExit.)

Bazı bloklar örtük istisna blokları oluşturur. Örneğin, yöntem tanımları örtük olarak da istisna bloklarıdır, bu yüzden yazmak yerine

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

sadece yaz

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

veya

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

Aynısı classtanımlar ve moduletanımlar için de geçerlidir .

Ancak, sorduğunuz belirli bir durumda, aslında çok daha iyi bir deyim vardır. Genel olarak, sonunda temizlemeniz gereken bir kaynakla çalıştığınızda, bunu sizin için tüm temizliği yapan bir yönteme geçirerek yaparsınız. usingC # 'daki bir bloğa benzer , ancak Ruby aslında Microsoft'un yüksek rahiplerinin dağdan inmesini ve derleyicisini sizin için nezaketle değiştirmesini beklemek zorunda kalmayacak kadar güçlüdür. Ruby'de sadece kendiniz uygulayabilirsiniz:

# 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

Ve ne biliyorsunuz: bu çekirdek kütüphanede zaten mevcut File.open. Ancak, kendi kodunuzda da kullanabileceğiniz, her türlü kaynak temizliğini ( usingC # 'da) veya işlemleri ya da aklınıza gelebilecek her şeyi uygulamak için kullanabileceğiniz genel bir kalıptır .

Kaynağın edinilmesi ve serbest bırakılması durumunda, bunun işe yaramadığı tek durum programın farklı bölümlerine dağıtılır. Ancak, örneğinizde olduğu gibi yerelleştirilmişse, bu kaynak bloklarını kolayca kullanabilirsiniz.


BTW: modern C # ' usingda aslında gereksizdir, çünkü Ruby tarzı kaynak bloklarını kendiniz uygulayabilirsiniz:

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İfadelerin son yürütülmüş olmasına rağmen , bunların dönüş değeri olmadığını unutmayın.
Chris

30
SO'da böyle zengin katkılar görmeyi seviyorum. OP'nin daha fazla geliştirici için geçerli olduğu şekilde sorduklarının ötesine geçiyor, ancak yine de konuyla ilgili. Bu cevap + düzenlemelerinden birkaç şey öğrendim. Sadece "Evet, ensurene olursa olsun çağrılır " yazdığınız için teşekkür ederiz .
Dennis

3
Unutmayın ki bu işlemin tamamlanacağı garanti EDİLMEZ. Bir iş parçacığının içinde bir başlangıç ​​/ sağlama / bitiş bulunduğunuz yeri ele alın ve daha sonra sağlama bloğunun ilk satırı çağrıldığında Thread.kill'i çağırın. Bu, geri kalan kısmın yürütülmemesine neden olur.
Teddy

5
@Teddy: yürütmeye başlandığından emin olun, tamamlanacağı garanti edilmez. Örneğiniz aşırı doldurmadır - sağlama bloğunun içindeki basit bir istisna da çıkmasına neden olacaktır.
Martin Konecny

3
Ayrıca, teminatın çağrıldığına dair hiçbir garanti bulunmadığını da unutmayın. Ciddiyim. Bir elektrik kesintisi / donanım hatası / işletim sistemi çökmesi meydana gelebilir ve yazılımınız kritik önem taşıyorsa, bunun da dikkate alınması gerekir.
EdvardM

37

Bilginize, rescuebölümde bir istisna yeniden yükseltilse bile ensure, kod yürütme bir sonraki istisna işleyiciye devam etmeden önce blok yürütülür. Örneğin:

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

14

Bir dosyanın kapalı olduğundan emin olmak istiyorsanız, aşağıdaki blok formunu kullanmalısınız File.open:

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

3
Hatayı işlemek istemiyorsanız, ancak yükseltip dosya tutamacını kapatmak istiyorsanız, burada kurtarmaya başlamanıza gerek yok mu?
rogerdpack


5

Evet, ensuresen gerekmez yüzden, her zaman çalıştırılır SAĞLAR file.closeiçinde beginbloğun.

Bu arada, test etmenin iyi bir yolu:

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

Bir istisna olduğunda "========= içeride blok sağlamak" yazdırılıp yazdırılmayacağını test edebilirsiniz. Ardından, hatayı yükselten ifadeyi yorumlayabilir ve ensureherhangi bir şeyin yazdırılıp yazdırılmadığını görerek ifadenin yürütülüp yürütülmediğini görebilirsiniz .


4

Bu yüzden ihtiyacımız var 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

Evet, ensureböyle finally garantiler blok çalıştırılacaktır o . Bu, kritik kaynakların korunduğundan emin olmak için çok kullanışlıdır, örneğin, hata durumunda bir dosya tanıtıcısını kapatmak veya bir muteksi serbest bırakmak.


Onun durumu dışında, dosyanın kapatılması için hiçbir garanti yoktur, çünkü File.openparça başlangıç ​​sağlama bloğunun içinde DEĞİLDİR. Sadece file.closeöyle ama yeterli değil.
Nowaker
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.