구조, 시작과 루비 확인?


547

최근에 Ruby로 프로그래밍을 시작했으며 예외 처리를보고 있습니다.

C #에서 ensureRuby와 동등한 지 궁금합니다 finally. 내가해야 :

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입니다. 따라서 Java 및 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루비는 실제로 Microsoft의 대제사장이 산에서 내려 와서 컴파일러를 자비 롭게 바꿀 때까지 기다릴 필요가 없을 정도로 강력하다는 점을 제외하면 C # 의 블록 과 비슷 합니다. 루비에서는 직접 구현할 수 있습니다.

# 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 #에서 la ) 또는 트랜잭션 또는 생각할 수있는 모든 것을 구현하기 위해 자신의 코드에서도 사용할 수있는 일반적인 패턴 입니다.

리소스를 확보하고 해제하는 것이 프로그램의 다른 부분에 분산되어있는 경우 이것이 작동하지 않는 유일한 경우입니다. 그러나 예제와 같이 현지화 된 경우 이러한 리소스 블록을 쉽게 사용할 수 있습니다.


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
나는 이와 같은 풍부한 기여를 보는 것을 좋아합니다. OP가 더 많은 개발자에게 적용 할 수 있도록 OP가 요구 한 것 이상으로 아직까지도 여전히 주제입니다. 이 답변 + 편집에서 몇 가지를 배웠습니다. "네, ensure아무리 불러도됩니다"라고 쓰지 말고 감사합니다 .
Dennis

3
완료가 보장되는 것은 아닙니다. 스레드 내부에 시작 / 확인 / 종료가있는 경우를 확인한 다음 확인 블록의 첫 번째 행이 호출 될 때 Thread.kill을 호출합니다. 이로 인해 나머지 확인이 실행되지 않습니다.
Teddy

5
@Teddy : 완료가 아니라 실행이 시작되도록 보장합니다. 귀하의 예는 과잉입니다-보장 블록 내부의 간단한 예외로 인해 종료됩니다.
Martin Konecny

3
또한 보장이 호출된다는 보장은 없습니다. 나는 진지하다. 정전 / 하드웨어 오류 / OS 충돌이 발생할 수 있으며 소프트웨어가 중요한 경우 해당 소프트웨어도 고려해야합니다.
EdvardM

37

참고로, 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 보장 블록이 실행됩니다 . 이는 오류시 파일 핸들을 닫거나 뮤텍스를 해제하는 등 중요한 리소스를 보호하는 데 매우 유용합니다.


그의 경우를 제외하고 파일 File.open이 시작 보장 블록 안에 있지 않기 때문에 파일을 닫을 것이라는 보장 은 없습니다. 만 file.close이지만 충분하지 않습니다.
Nowaker
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.