Что такое Managed C++, эквивалентный оператору using C#?

Как можно закодировать следующий код C# в Managed C++

void Foo()
{
    using (SqlConnection con = new SqlConnection("connectionStringGoesHere"))
    {
         //do stuff
    }
}

Пояснение: для управляемых объектов.


person Adam Tegen    schedule 03.12.2008    source источник


Ответы (4)


Предполагая, что вы имеете в виду C++/CLI (а не старый Managed C++), у вас есть следующие варианты:

(1) Имитация использования блока с использованием автоматических/стековых объектов:

{
  SqlConnection conn(connectionString);
}

Это вызовет деструктор объекта "conn", когда закончится следующий объемлющий блок. Не имеет значения, является ли это закрывающей функцией или блоком, который вы вручную добавляете для ограничения области действия.

(2) Явно вызовите «Dispose», т.е. уничтожьте объект:

SqlConnection^ conn = nullptr;
try
{
  conn = gcnew SqlConnection(conntectionString);

}
finally
{
  if (conn != nullptr)
    delete conn;
}

Первый будет прямой заменой «использования». Второй - это вариант, обычно вам не нужно делать это, если вы не передадите ссылку куда-то еще.

person Christian.K    schedule 04.12.2008
comment
Является ли первый синтаксис (использующий голые фигурные скобки для ограничения области действия) гарантированным вызовом Dispose, даже если вы выходите из области видимости, вызывая исключение? Я не думал, что это так, но, конечно, я мог ошибаться. - person Coderer; 04.12.2008
comment
Да, это гарантировано. Собственно, вот в чем идея. Деструкторы объектов, размещенных в стеке, вызываются, когда закрывающая область заканчивается (либо регулярно, либо преждевременно по исключению) - на самом деле это не имеет ничего общего с управляемым или нет. Так же и в нативном коде. - person Christian.K; 05.12.2008
comment
@Christian.K, вы уверены, если не передадите ссылку куда-то еще? Я думаю, что пример (1) был бы хорош даже в этом случае. - person JoelFan; 07.11.2011
comment
@JoelFan Честно говоря, я не могу вспомнить, что я изначально упоминал здесь ;-) Конечно, вы правы: вы можете передать объект из (1) и в другие места. Я мог только предположить, что я имею в виду, что во втором случае вы также можете передать опцию удаления объекта куда-то еще (т.е. позволить кому-то другому взять на себя управление им). - person Christian.K; 08.11.2011
comment
Следует отметить, что когда переменная выходит за пределы области видимости, она ставится в очередь для сборки мусора, но фактическая сборка мусора может произойти позже. Таким образом, если важно, чтобы очистка происходила до того, как вы потеряете область действия, вы захотите сделать это явно, а не ждать деструктора/финализатора. Недавно у меня был пример этого, когда я писал в файловый поток и не вызывал явным образом stream.Close(). Я обнаружил, что поток не был полностью очищен до определенного времени (т. е. до запуска сборщика мусора), и это вызывало проблемы. Решение состояло в том, чтобы добавить явный вызов stream.Close() до того, как поток выйдет за пределы области видимости. - person dlchambers; 17.11.2011
comment
@dlchambers У меня нет здесь практического опыта, но, насколько мне известно, деструкторы детерминированы в C++ /CLI. т.е. когда вызывается деструктор, на самом деле вызывается Dispose. Поэтому, если у вас есть тип, который правильно реализует IDisposable, все должно быть в порядке. т.е. время фактического GC, которое не имеет ничего общего с Dispose как таковым, не имеет значения, потому что фактическая очистка происходит (детерминированная) в той точке кода, которую вы ожидаете (var выходит за рамки). - person Christian.K; 18.11.2011
comment
Фактически, в собственном С++ деструктор гарантированно вызывается только в том случае, если исключение перехвачено - stackoverflow.com/q/8311457/986 (хотя очевидно, что если его не поймать, приложение все равно рухнет). - person Mark Ingram; 02.02.2015
comment
Я люблю это. Как заставить C# и VB.net делать это!? - person Brain2000; 04.12.2018

Для этого в Managed C++ просто используйте семантику стека.

void Foo(){
   SqlConnection con("connectionStringGoesHere");
    //do stuff
}

Когда con выходит за пределы области действия, вызывается "Destructor", т.е. Dispose().

person jyoung    schedule 04.12.2008

Вы можете сделать нечто похожее в стиле auto_ptr:

void foo()
{
    using( Foo, p, gcnew Foo() )
    {
        p->x = 100;
    }
}

со следующим:

template <typename T>
public ref class using_auto_ptr
{
public:
    using_auto_ptr(T ^p) : m_p(p),m_use(1) {}
    ~using_auto_ptr() { delete m_p; }
    T^ operator -> () { return m_p; }
    int m_use;
private:
    T ^ m_p;
};

#define using(CLASS,VAR,ALLOC) \
    for ( using_auto_ptr<CLASS> VAR(ALLOC); VAR.m_use; --VAR.m_use)

Для справки:

public ref class Foo
{
public:
    Foo() : x(0) {}
    ~Foo()
    {
    }
    int x;
};
person Nick    schedule 23.03.2009

Если вы беспокоитесь об ограничении времени жизни переменной, а не об автоматическом удалении, вы всегда можете просто поместить ее в свою область видимости:

void Foo()
{
    {
        SqlConnection con = new SqlConnection("connectionStringGoesHere");
        // do stuff
        // delete it before end of scope of course!
    }
}
person Mike Hall    schedule 03.12.2008
comment
Это не вызовет ни деструктора в конце области видимости, ни Dispose(). В этом смысле он имеет тот же эффект, что и в C#. - person Christian.K; 04.12.2008
comment
Да, ты прав. Это не будет. Я предполагал, что это будет сделано в части работы. Все, что я указывал, это то, что мошенник не может быть доступен за пределами этой новой области. - person Mike Hall; 07.12.2008