Wednesday, March 16, 2011

What is the Managed C++ equivalent to the C# using statement

How would one code the following C# code in Managed C++

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

Clarificaton: For managed objects.

From stackoverflow
  • I don't think, there is some syntax sugare like "using( ... ) { ... }";

    You can write only:

    SqlConnection cnn = new SqlConnection( ... );
    try {
        ...
    }
    finally {
        cnn->Dispose();
    }
    
    Christian.K : Wrong, see "jyoung" answer. Besides you cannot call "Dispose()", but must use "delete cnn", and new should be gcnew.
    TcKs : Yes, you right with delete, but the "Dispose()" should be called before, or not?
    Christian.K : No, actually it is not accessible (not public). In C++/CLI, the destructor *is* the Dispose() operation.
    Robert S. : Tip: Never guess an answer on SO.
  • If you're concerned about limiting the variable's lifetime rather than automatic disposal, you can always just put it into its own scope:

    void Foo()
    {
        {
            SqlConnection con = new SqlConnection("connectionStringGoesHere");
            // do stuff
            // delete it before end of scope of course!
        }
    }
    
    Christian.K : This will neither call the destructor at the end of the scope, nor will "Dispose()" be called. It that sense it has the same effect as in C#.
    Mike Hall : Yes, you're correct. It won't. I assumed that would be done in the "do stuff" part. All I was pointing out is that con can't be accessed outside of that new scope.
  • To to that in Managed C++ just use stack semantics.

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

    When con goes out of scope the "Destructor", ie Dispose(), is called.

    orip : +1, http://en.wikipedia.org/wiki/Resource_acquisition_is_initialization
  • Assuming you mean C++/CLI (not the old Managed C++), the following are your options:

    (1) Mimic a using-Block with using automatic / stackbased objects:

    {
      SqlConnection conn(connectionString);
    }
    

    This will call the Destructor of the "conn" Object when the next enclosing block ends. Whether this is the enclosing function, or a block you manually add to limit scope doesn't matter.

    (2) Explicitly call "Dispose", i.e. destruct the object:

    SqlConnection^ conn = nullptr;
    try
    {
      conn = gcnew SqlConnection(conntectionString);
    
    }
    finally
    {
      if (conn != nullptr)
        delete conn;
    }
    

    The first one would be the direct replacement for "using". The second one is an option, typically you won't need to do unless you optionally pass the reference to somewhere else.

    Coderer : Is the first syntax (using bare curly-braces to limit scope) guaranteed to call Dispose even if you leave scope by throwing an exception? I did not think that was the case, but of course I could be wrong.
    Christian.K : Yes its guaranted. Indeed that is the idea here. Destructors of stack-allocated objects are called when the enclosing scope ends (either regularly or prematurely by an exception) - actually that has nothing to do with managed or not. It is also this way in native code.
  • You could do something similar in an auto_ptr style:

    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)
    

    For reference:

    public ref class Foo
    {
    public:
        Foo() : x(0) {}
        ~Foo()
        {
        }
        int x;
    };
    

0 comments:

Post a Comment