4

解放する必要のあるリソース (ファイルハンドルやネットワークソケットなど) があるとします。

open my $fh, "<", "filename" or die "Couldn't open filename: $!";
process($fh);
close $fh or die "Couldn't close filename: $!";

それprocessが死ぬかもしれないとしましょう。その後、コード ブロックは早期に終了し、$fh閉じられません。

エラーを明示的にチェックできます。

open my $fh, "<", "filename" or die "Couldn't open filename: $!";
eval {process($fh)};
my $saved_error = $@;
close $fh or die "Couldn't close filename: $!";
die $saved_error if $saved_error;

しかし、この種のコードは正しく作成するのが難しいことで有名であり、リソースを追加するとさらに複雑になります。

C++ では、 RAIIを使用して、リソースを所有し、そのデストラクタがそれを解放するオブジェクトを作成します。そうすれば、リソースを解放することを覚えておく必要がなくなり、例外がスローされた場合でも、RAII オブジェクトが範囲外になるとすぐにリソースのクリーンアップが正しく行われます。残念ながら、Perl ではDESTROYメソッドが呼び出されるタイミングが保証されていないため、メソッドはこの目的には適していません。

例外が存在する場合でも、このようにリソースが自動的に解放されるようにする Perlish の方法はありますか? それとも明示的なエラーチェックが唯一のオプションですか?

4

3 に答える 3

4

それが、 Scope::Guardが支援するために設計されたものだと思います。

#!/usr/bin/perl

use strict; use warnings;
use Scope::Guard;

my $filename = 'file.test';

open my $fh, '>', $filename
    or die "Couldn't open '$filename': $!";

{
    my $sg = Scope::Guard->new(
        sub {
            close $fh or die "Could not close";
            warn "file closed properly\n";
        }
    );

    process($fh);
}

sub process { die "cannot process\n" }

ただし、@Philip がコメントで指摘しているように、スコープ終了コードがいつ実行されるかについて不確実性を生み出すメソッドをScope::Guard利用しています。DESTROYやなどのモジュールも、使用したことがありませんがHook::ScopeSub::ScopeFinalizer見栄えがします。

私はTry::Tinyが好きです。そのクリーンなインターフェイスとシンプルさから、例外を正しい方法で処理するのに役立ちます。

#!/usr/bin/perl

use strict; use warnings;
use Try::Tiny;

my $filename = 'file.test';

open my $fh, '>', $filename
    or die "Couldn't open '$filename': $!";

try {
    process($fh);
}
catch {
    warn $_;
}
finally {
    close $fh
        and warn "file closed properly\n";
};

sub process { die "cannot process\n" }
于 2010-03-23T16:40:56.803 に答える
4

私のモジュールScope::OnExitはまさにそれを目的としています。

于 2010-03-23T18:14:07.930 に答える
3

レキシカル ファイルハンドルの良いところは、スコープ外に出ると閉じられる (そして解放される) ことです。したがって、次のようなことができます。

{
    # bare block creates new scope
    open my $fh, "<", "filename" or die "Couldn't open filename: $!";
    eval { process($fh) };

    # handle exceptions here

    close $fh or die "Couldn't close filename: $!";
}

# $fh is now out of scope and goes away automagically.
于 2010-03-23T16:45:29.707 に答える