首页 文章

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

提问于
浏览
18

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

6 回答

  • 5

    免责声明我也是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();
    }
    
  • 6

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

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

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

  • 39

    scope(X) 不一定需要 for ,只要你有 ifgoto .

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

    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)的版本更易读,更容易理解 .

  • 2

    值得一提的是,范围(退出),范围(失败)和范围(成功)也可用于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
    
  • 9

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

    当我解释这个新的Python特性(它现在已经存在一段时间了;-)给了C和Java大师的朋友和同事时,我发现他们立即理解了,并且看到了对这样一个特性的兴趣(Python确实有 finally ,但是's no help in distinguishing success from failure, just like in other languages [or C++' s "RAII destruction of auto variables in the block"等效]) .

  • 5

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

    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部分隐藏起来,远离其他部分(在范围(失败)的情况下)并制作资源管理透明(在范围(退出)的情况下) .

相关问题