D的范围是否失败/成功/退出是否必要?

时间:2009-08-08 02:18:40

标签: c# c++ d

当使用具有try / catch / finally的语言时,D的失败/成功/退出范围语句是否仍然有用? D似乎没有最终可以解释为什么在D中使用这些语句。但是使用像C#这样的语言是否有用?我正在设计一种语言,所以如果我看到很多专业人士,我会加入它。

6 个答案:

答案 0 :(得分:39)

scope(X)forif相同,不需要goto

这是我今天写的一些代码中的一个释义示例:

sqlite3* db;
sqlite3_open("some.db", &db);
scope(exit) sqlite3_close(db);

sqlite3_stmt* stmt;
sqlite3_prepare_v2(db, "SELECT * FROM foo;", &stmt);
scope(exit) sqlite3_finalize(stmt);

// Lots of stuff...

scope(failure) rollback_to(current_state);
make_changes_with(stmt);

// More stuff...

return;

将此与使用try / catch对比:

sqlite3* db;
sqlite3_open("some.db", &db);
try
{
    sqlite3_stmt* stmt;
    sqlite3_prepare_v2(db, "SELECT * FROM foo;", &stmt);
    try
    {
        // Lots of stuff...
        try
        {
            make_changes_with(stmt);

            // More stuff...
        }
        catch( Exception e )
        {
            rollback_to(current_state);
            throw;
        }
    }
    finally
    {
        sqlite3_finalize(stmt);
    }
}
finally
{
    sqlite3_close(db);
}

代码已变为spaghetti,在整个商店中传播错误恢复,并强制每个try块的缩进级别。在我看来,使用范围(X)的版本更具可读性和易于理解。

答案 1 :(得分:9)

try / catch /最终强制嵌套;范围守卫没有。此外,它们允许您在与分配代码相同的“区域”中编写清理代码,因此不再需要“打开文件,滚动到功能结束,关闭文件,滚动到功能顶部”。

从根本上说,它只是一个更方便的表达try / catch / finally异常处理 - 任何你可以用try / catch /最后你可以用范围保护,并反向。

值得吗?我是一个D粉丝(所以,有偏见),但我肯定会说。

答案 2 :(得分:6)

免责声明我也是D粉丝。

someRiskyFunctionThatMayThrow();
lock();
/* we have definitly got the lock so lets active
a piece of code for exit */
scope(exit)
    freelock();

与:相比:

try
{
    someRiskyFunctionThatMayThrow();
    lock();
}
finally
{
    freeLockIfNotGot();
}

答案 3 :(得分:5)

区分失败 - 退出和成功 - 退出在某些时候是非常有用的 - 我没有D的真实世界经验,但Python的with语句也允许这样,我发现它非常有用,例如,提交或回滚在正文的受保护部分中打开的数据库事务。

当我解释这个新的Python特性(现在已经存在一段时间了;-)给了C ++和Java大师的朋友和同事时,我发现他们立刻明白了,并且看到了对这种功能的兴趣( Python确实有finally,但这对于区分成功和失败没有帮助,就像在其他语言中一样[或者C ++的“RAII破坏块中的自动变量”等等]。

答案 4 :(得分:5)

值得一提的是,范围(退出),范围(失败)和范围(成功)也适用于C ++。

支持以下语法,案例1:

try
{
    int some_var=1;
    cout << "Case #1: stack unwinding" << endl;
    scope(exit)
    {
        cout << "exit " << some_var << endl;
        ++some_var;
    };
    scope(failure)
    {
        cout << "failure " << some_var  << endl;
        ++some_var;
    };
    scope(success)
    {
        cout << "success " << some_var  << endl;
        ++some_var;
    };
    throw 1;
} catch(int){}

打印:

Case #1: stack unwinding
failure 1
exit 2

案例2:

{
    int some_var=1;
    cout << "Case #2: normal exit" << endl;
    scope(exit)
    {
        cout << "exit " << some_var << endl;
        ++some_var;
    };
    scope(failure)
    {
        cout << "failure " << some_var << endl;
        ++some_var;
    };
    scope(success)
    {
        cout << "success " << some_var << endl;
        ++some_var;
    };
}

打印:

Case #2: normal exit
success 1
exit 2

答案 5 :(得分:2)

@DK,应该指出,在C ++(和我认为的Java)中,您可以轻松地使用“匿名”类来完成与范围(退出)相同的事情:

int some_func() 
{
    class _dbguard { sqlite3* db;
                     _dbguard(const _dbguard&); _dbguard& operator=(const _dbguard&);
                 public:
                     _dbguard(const char* dbname) { sqlite3_open(dbname, &db);}
                     ~_dbguard() {sqlite3_close(db);} 
                     operator sqlite3*() { return db; } 

    } db("dbname");
    ...
}

如果您不止一次这样做,您会立即将其变成一个完整的课程来为您处理RAII。编写起来非常简单我无法想象使用sqlite的C ++程序(如示例中所使用的)而不创建类似CSqlite_DB和CSqlite_Stmt的类。事实上,运算符sqlite3 *()应该是anathama,完整版本只有提供语句的方法:

class CSqlite3_DB {
    ...
    CSqlite3_Stmt Prepare(const std::string& sql) {
        sqlite3_stmt* stmt = 0;
        try {
             sqlite3_prepare_v2(db, sql.c_str(), &stmt);
        } catch (...) {}
        return stmt;
    }
};

至于原来的问题,我会说答案是“不是真的”。正确尊重DRY会告诉你把那些长的try / catch / finally块转换成单独的类,将try / catch部分隐藏起来,远离其他部分(在范围(失败)的情况下)并使资源管理透明(在范围(退出)的情况​​下)。