Действительно ли отказ/успех/выход объема D необходим?

Используйте Насмешки Носорога для установки, какие вызовы, действия и исключения могли бы ожидаться. Принятие Вас может дразнить или погасить части Вашего метода. Трудно знать, не зная некоторых специфических особенностей здесь о методе, или даже контексте.

18
задан Evgeny Panasyuk 9 November 2012 в 19:12
поделиться

5 ответов

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), на мой взгляд, значительно более читабельна и проще для понимания.

39
ответ дан 30 November 2019 в 05:53
поделиться

try / catch / finally принудительно устанавливает уровень вложенности; охранники прицела нет. Кроме того, они позволяют вам писать код очистки в той же «области», что и код выделения, поэтому больше не нужно «открывать файл, прокручивать до конца функции, закрывать файл, прокручивать до начала функции».

По сути, это просто более удобное выражение обработки исключений try / catch / finally - все, что вы можете сделать с помощью try / catch / finally, вы можете сделать с защитой области видимости и реверсом.

Стоит ли оно того? Я фанат D (так предвзято), но я бы сказал определенно.

9
ответ дан 30 November 2019 в 05:53
поделиться

Отказ от ответственности 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();
}
6
ответ дан 30 November 2019 в 05:53
поделиться

Отличить выход-сбой от выхода-успеха иногда бывает очень полезно - у меня нет реального опыта работы с D, но оператор Python with также позволяет это, и я считаю очень полезным, например, либо зафиксировать, либо откатить транзакцию БД, которая была открыта в защищенной части тела.

Когда я объяснил эту тогда еще новую функцию Python (она существует уже некоторое время; -) друзьям и коллегам, которые являются гуру в C ++ и Java. Я обнаружил, что они сразу поняли и увидели интерес в наличии такой функции (Python действительно имеет , наконец, , но это не помогает отличить успех от сбой, как и в других языках [или эквивалент C ++ "RAII-уничтожение автоматических переменных в блоке"]).

5
ответ дан 30 November 2019 в 05:53
поделиться

@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 подальше от остальных, где они могут (в случае области видимости (сбой)) и делают прозрачное управление ресурсами (в случае области действия (выхода)).

2
ответ дан 30 November 2019 в 05:53
поделиться
Другие вопросы по тегам:

Похожие вопросы: