当使用具有try / catch / finally的语言时,D的失败/成功/退出范围语句是否仍然有用? D似乎没有最终可以解释为什么在D中使用这些语句。但是使用像C#这样的语言是否有用?我正在设计一种语言,所以如果我看到很多专业人士,我会加入它。
答案 0 :(得分:39)
scope(X)
与for
和if
相同,不需要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部分隐藏起来,远离其他部分(在范围(失败)的情况下)并使资源管理透明(在范围(退出)的情况下)。