我应该将“eval”放在子程序或“eval”中的子程序中吗?

时间:2012-09-28 07:26:54

标签: perl error-handling eval

这两种方式中的一种是否必须首选,还是仅仅是品味问题?

#!/usr/bin/env perl
use warnings;
use strict;
use DBI;

my $db = 'sqlite_db';

####################   A   ####################

sub get_database_handle {
    my ( $db ) = @_;
    my $dbh;
    eval {
        $dbh = DBI->connect( "DBI:SQLite:$db", '', '', {...} )
        or die DBI->errstr;
    };
    if ( $@ ) {
        print $@;
        return;
    }
    return $dbh;
}

DATABASES: while ( 1 ) {
    # choose a database from a list of databases
    # ...
    my $dbh = get_database_handle( $db );
    next DATABASES if not defined $dbh;
    # ...
    # do something with the database
}

####################   B   ####################

sub get_database_handle {
    my ( $db ) = @_;
    my $dbh = DBI->connect( "DBI:SQLite:$db", '', '', {...} ) 
    or die DBI->errstr;
    return $dbh;
}

DATABASES: while ( 1 ) {
    # choose a database from a list of databases
    # ...
    my $dbh;
    eval { $dbh = get_database_handle( $db ); };
    if ( $@ ) {
        print $@;
        next DATABASES;
    }
    # ...
    # do something with the database
}

3 个答案:

答案 0 :(得分:3)

为什么要评估?当你无法获得数据库句柄时,你打算做什么?

至少,子程序应该返回一个数据库句柄或者死掉,所以那里没有eval。

如果在数据库错误时确实有一些工作要做,那么在子程序之外进行eval。对于您的错误处理逻辑,仅在子例程周围不一定可能是更宽的范围。

但如果你想要的只是终止程序并输出错误,那就让异常冒泡。

答案 1 :(得分:2)

这取决于在项目其余部分处理错误的首选方式。

  • 如果您打算使用例外,请让您的函数抛出它们。

  • 如果您要通过条件手动处理错误,请不要抛出(eval inside)。

我自己更喜欢例外。它们很响亮(你知道有些东西坏了!),加上通过Carp::confess / Carp::longmess$SIG{__DIE__}的堆栈跟踪在大型代码库的情况下是一个很好的奖励。

这是一个(非)成功的故事。一两个月前,我们推出了破坏的代码,由于来自DB处理函数的未经检查的返回值而导致数据无声地损坏。它正在生产一天。恢复数据是一个痛苦的世界。

答案 2 :(得分:2)

如果您的代码看起来像那样,那么完全使用RaiseError => 1是没有意义的。如果您想保留该代码布局,请改用RaiseError => 0。 (您可以随后使用$ dbh-> {RaiseError} = 1;`来启用它。)

sub get_database_handle {
    my ( $db ) = @_;
    return DBI->connect("DBI:SQLite:$db", '', '', {
        ...
        RaiseError => 0,
        PrintError => 1,
    });
}

for my $db ( ... ) {
    my $dbh = get_database_handle( $db )
       or next;

    ...
}

那就是说,我建议你继续使用RaiseError => 1,但是你改变了你的代码布局。具体来说,您应该扩大评估的范围。

sub get_database_handle {
    my ( $db ) = @_;
    return DBI->connect("DBI:SQLite:$db", '', '', {
        ...
        RaiseError => 1,
        PrintError => 0,
    });
}

for my $db ( ... ) {
    if (!eval {    
        my $dbh = get_database_handle( $db );

        ...

        1  # No exception
    }) {
        warn("Error processing database $db: $@");
    }
}

这将捕获任何错误,而不仅仅是数据库连接错误。