Используйте Насмешки Носорога для установки, какие вызовы, действия и исключения могли бы ожидаться. Принятие Вас может дразнить или погасить части Вашего метода. Трудно знать, не зная некоторых специфических особенностей здесь о методе, или даже контексте.
scope (X)
не требуется точно так же, как для
не требуется, если у вас есть 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);
}
Код превратился в спагетти , распространение восстановления после ошибок по всему магазину и принудительный уровень отступа для каждого блока попытки. Версия, использующая область видимости (X), на мой взгляд, значительно более читабельна и проще для понимания.
try / catch / finally принудительно устанавливает уровень вложенности; охранники прицела нет. Кроме того, они позволяют вам писать код очистки в той же «области», что и код выделения, поэтому больше не нужно «открывать файл, прокручивать до конца функции, закрывать файл, прокручивать до начала функции».
По сути, это просто более удобное выражение обработки исключений try / catch / finally - все, что вы можете сделать с помощью try / catch / finally, вы можете сделать с защитой области видимости и реверсом.
Стоит ли оно того? Я фанат D (так предвзято), но я бы сказал определенно.
Отказ от ответственности I ' Я тоже фанат 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();
}
Отличить выход-сбой от выхода-успеха иногда бывает очень полезно - у меня нет реального опыта работы с D, но оператор Python with
также позволяет это, и я считаю очень полезным, например, либо зафиксировать, либо откатить транзакцию БД, которая была открыта в защищенной части тела.
Когда я объяснил эту тогда еще новую функцию Python (она существует уже некоторое время; -) друзьям и коллегам, которые являются гуру в C ++ и Java. Я обнаружил, что они сразу поняли и увидели интерес в наличии такой функции (Python действительно имеет , наконец,
, но это не помогает отличить успех от сбой, как и в других языках [или эквивалент C ++ "RAII-уничтожение автоматических переменных в блоке"]).
@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 за вас. Это настолько просто написать, что я не могу представить себе программу на C ++, которая использует sqlite (используемый в примере) без создания таких классов, как CSqlite_DB и CSqlite_Stmt. Фактически, оператор sqlite3 * () должен быть анатамом, а полная версия будет содержать только методы, которые предоставляют операторы:
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 подальше от остальных, где они могут (в случае области видимости (сбой)) и делают прозрачное управление ресурсами (в случае области действия (выхода)).