Как можно написать следующий код C# в управляемом C++
void Foo()
{
using (SqlConnection con = new SqlConnection("connectionStringGoesHere"))
{
//do stuff
}
}
Разъяснение: Для управляемых объектов.





Если вас беспокоит ограничение времени жизни переменной, а не автоматическое удаление, вы всегда можете просто поместить ее в отдельную область:
void Foo()
{
{
SqlConnection con = new SqlConnection("connectionStringGoesHere");
// do stuff
// delete it before end of scope of course!
}
}
Да, ты прав. Не будет. Я предполагал, что это будет сделано в части «что-то делать». Все, что я указывал, это то, что к con нельзя получить доступ за пределами этой новой области.
Для этого в Managed C++ просто используйте семантику стека.
void Foo(){
SqlConnection con("connectionStringGoesHere");
//do stuff
}
Когда con выходит за пределы области видимости, вызывается «Деструктор», то есть Dispose ().
Предполагая, что вы имеете в виду C++ / CLI (а не старый управляемый C++), у вас есть следующие варианты:
(1) Имитируйте using-Block с использованием автоматических / основанных на стеке объектов:
{
SqlConnection conn(connectionString);
}
Это вызовет деструктор объекта "conn", когда закончится следующий включающий блок. Не имеет значения, является ли это закрывающей функцией или блоком, который вы вручную добавляете для ограничения области действия.
(2) Явно вызовите «Dispose», т. Е. Уничтожьте объект:
SqlConnection^ conn = nullptr;
try
{
conn = gcnew SqlConnection(conntectionString);
}
finally
{
if (conn != nullptr)
delete conn;
}
Первый - это прямая замена слова «использование». Второй вариант - это вариант, обычно вам не нужно этого делать, если вы не передадите ссылку в другое место.
Гарантированно ли первый синтаксис (с использованием фигурных скобок для ограничения области действия) вызовет Dispose, даже если вы покидаете область действия, создав исключение? Я не думал, что это так, но, конечно, мог ошибаться.
Да, это гарантировано. Действительно, идея здесь. Деструкторы объектов, выделенных стеком, вызываются, когда закрывающая область действия заканчивается (регулярно или преждевременно из-за исключения) - на самом деле это не имеет ничего общего с управляемыми или нет. Так же и в машинном коде.
@ Christian.K, вы уверены, что "если вы не передадите ссылку в другое место"? Я думаю, что пример (1) подойдет даже в этом случае.
@JoelFan Честно говоря, я не могу вспомнить, что я здесь изначально имел в виду ;-) Конечно, вы правы: вы также можете передать объект из (1) в другие места. Я мог только предположить, что во втором случае вы также можете «передать возможность» удалить объект в другое место (т.е. позволить кому-то другому взять на себя управление им на протяжении всей жизни).
Следует отметить, что когда var выходит за пределы области видимости, это в очереди для GC, но фактический GC может произойти «позже». Таким образом, если важно, чтобы очистка произошла до того, как вы потеряете область видимости, вы захотите сделать это явно, вместо того, чтобы ждать деструктора / финализатора. Недавно у меня был пример этого, когда я писал в файловый поток, а не вызывал явно stream.Close (). Я обнаружил, что поток не был полностью сброшен до «некоторого более позднего времени» (т.е. когда запускался сборщик мусора), и это вызывало проблемы. Решением было добавить явный вызов stream.Close () до того, как поток выйдет за пределы области видимости.
@dlchambers У меня здесь нет практического опыта, но AFAIK деструкторы детерминированы в C++ / CLI. Т.е. когда вызывается деструктор, на самом деле вызывается Dispose. Так что, если у вас есть тип, который "правильно" реализует IDisposable, все будет в порядке. Т.е. время фактического GC, который не имеет ничего общего с Dispose как таковой, не имеет значения, потому что фактическая очистка происходит (детерминированная) в той точке кода, которую вы ожидаете («var выходит за рамки»).
На самом деле, в родном C++ деструктор гарантированно будет вызываться только в случае обнаружения исключения - stackoverflow.com/q/8311457/986 (хотя, очевидно, если он не пойман, приложение все равно выйдет из строя).
Я люблю это. Как мне заставить C# и VB.net сделать это !?
Вы можете сделать что-нибудь похожий в стиле 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;
};
#include <iostream>
using namespace std;
class Disposable{
private:
int disposed=0;
public:
int notDisposed(){
return !disposed;
}
void doDispose(){
disposed = true;
dispose();
}
virtual void dispose(){}
};
class Connection : public Disposable {
private:
Connection *previous=nullptr;
public:
static Connection *instance;
Connection(){
previous=instance;
instance=this;
}
void dispose(){
delete instance;
instance = previous;
}
};
Connection *Connection::instance=nullptr;
#define using(obj) for(Disposable *__tmpPtr=obj;__tmpPtr->notDisposed();__tmpPtr->doDispose())
int Execute(const char* query){
if (Connection::instance == nullptr){
cout << "------- No Connection -------" << endl;
cout << query << endl;
cout << "------------------------------" << endl;
cout << endl;
return -1;//throw some Exception
}
cout << "------ Execution Result ------" << endl;
cout << query << endl;
cout << "------------------------------" << endl;
cout << endl;
return 0;
}
int main(int argc, const char * argv[]) {
using(new Connection())
{
Execute("SELECT King FROM goats");//out of the scope
}
Execute("SELECT * FROM goats");//in the scope
}
Это не вызовет ни деструктора в конце области видимости, ни Dispose (). В этом смысле он имеет тот же эффект, что и в C#.