Подтвердить что ты не робот

Требуется ли отказ или необходимость/выход D-области?

При использовании языка, в котором есть try/catch/finally, есть еще аргументы D сбой/успех/выход? D, похоже, не может окончательно объяснить, почему эти утверждения используются в D. Но с таким языком, как С#, это полезно? Я разрабатываю язык, поэтому, если вижу много профи, я его добавлю.

4b9b3361

Ответ 1

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

Ответ 2

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

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

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

Ответ 3

Отказ от ответственности Я тоже мальчик-фанат 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();
}

Ответ 4

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

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

Ответ 5

Стоит отметить, что для С++ доступны также область (выход), область (сбой) и область (успех).

Поддерживается следующий синтаксис: case 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

Ответ 6

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