Managed C++中与C#中的using语句相当的是什么?

24

如何在托管C++中编写以下C#代码:

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

澄清: 针对托管对象。

6个回答

35

假设您是指C++/CLI(而不是旧版Managed C++),以下是您的选择:

(1)使用自动/基于栈的对象来模拟using块:

{
  SqlConnection conn(connectionString);
}

当下一个封闭块结束时,这将调用“conn”对象的析构函数。无论这是包含函数还是您手动添加以限制范围的块都无所谓。

(2) 显式调用“Dispose”,即销毁该对象:

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

}
finally
{
  if (conn != nullptr)
    delete conn;
}
第一个选项是“using”的直接替代。第二个选项是可选项,通常情况下您不需要这样做,除非您将参考文献选择传递到其他地方。

1
第一种语法(使用裸花括号来限制作用域)是否保证即使通过抛出异常离开作用域也会调用Dispose?我认为不是这样,但我当然可能错了。 - Coderer
@Christian.K,你确定关于“除非你选择将引用传递到其他地方”吗?我认为即使在那种情况下,例子(1)也是可以的。 - JoelFan
@JoelFan 坦白地说,我不记得我最初在这里的意思是什么了;-) 当然,你是正确的:你也可以将对象从(1)传递到其他地方。我只能假设在第二种情况下,我也想“传递选项”到别的地方,即让其他人接管其生命周期管理并删除该对象。 - Christian.K
4
需要翻译的内容:需要注意的一点是,当变量超出作用域时,它会被“排队”等待垃圾回收,但实际的垃圾回收可能会“延迟”发生。因此,如果重要的是在失去作用域之前进行清理,你就需要显式地进行操作,而不是等待析构函数/终结器。我最近有一个例子,我正在向文件流中写入数据,但没有显式地调用stream.Close()。我发现,在“某个未来时间”(即垃圾回收运行时),流并没有完全刷新,这导致了问题。解决方法是在 stream 超出作用域之前添加一个显式的 stream.Close() 调用。 - dlchambers
3
据我所知,在C++/CLI中,析构函数是确定性的。也就是说,当调用析构函数时,实际上调用的是 Dispose 函数。因此,如果您有一个“正确”实现了 IDisposable 接口的类型,那么就应该没问题了。也就是说,实际的垃圾回收时间与 Dispose 并没有什么关系,因为实际的清理是在代码中您期望的地方(“变量超出范围”的点)发生的(确定性)。 - Christian.K
显示剩余3条评论

3
在托管C++中,只需使用堆栈语义即可实现此操作。
void Foo(){
   SqlConnection con("connectionStringGoesHere");
    //do stuff
}

当con超出范围时,“析构函数”即Dispose()被调用。

2
您可以使用类似auto_ptr的方式来实现类似的功能:
void foo()
{
    using( Foo, p, gcnew Foo() )
    {
        p->x = 100;
    }
}

with the following:

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;
};

0
#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

}

0
我认为在这里使用基于栈的对象是最明智的选择。如果你不能直接调用构造函数,可以使用auto_handle代替。
#include <msclr/auto_handle.h>

void Foo()
{
    msclr::auto_handle<MyDisposableType> myDisposable(CSharpClass::CreateDisposable());
    myDispoable->DoStuff();
    // myDisposable gets disposed here
}

这个答案对这个问题有很多有用的信息:C++/CLI资源管理混淆

-3

如果您关心的是限制变量的生命周期而不是自动处理,您可以将其放入自己的作用域中:

void Foo()
{
    {
        SqlConnection con = new SqlConnection("connectionStringGoesHere");
        // do stuff
        // delete it before end of scope of course!
    }
}

1
这将不会在作用域结束时调用析构函数,也不会调用“Dispose()”。从这个意义上讲,它与C#中的效果相同。 - Christian.K
是的,你说得对。它不会这样做。我假设那将在“做事情”的部分完成。我指出的只是con无法在新范围之外访问。 - Mike Hall

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接