Question

A colleague of mine came across an article on codeproject over the weekend which describes the use of the auto_handle method. Given that the article was written in 2006, is that still the right way to handle deterministic disposal for things like file handles?

We've tended to explicitly flush and close file handles and then delete the pointers and null them to be certain the GC has no excuse not to collect them (yes, we know we're super-paranoid). Using something like auto_handle looks like it could help us be more lazy (and we like being lazy when it's safe to do so) but we don't want to start using it's considered bad practice these days and/or there's something better we can use.

Was it helpful?

Solution

C++/CLI hasn't changed since the 2005 release, so no reason to assume anything new happened. Same advice as was valid back in 2005, auto_handle is not the best way to auto-delete objects. You should consider stack semantics first, it covers the vast majority of cases. The compiler auto-generates the try/finally blocks and the delete call (i.e. disposes the object), equivalent to the C# using statement, minus the code. The example given in the link is done like this:

void Demo1A()
{
    StreamWriter sw("c:\\temp\\test.txt");
    sw.WriteLine("This is a line of text");
}   // <=== Compiler auto-generates the StreamWriter::Dispose call here

Note the missing ^ hat on the object declaration.

And do consider that this has nothing at all to do with the garbage collector, only with deterministic resource cleanup. Also note that setting local variables to nullptr is very inappropriate, it can extend the lifetime of a reference beyond its actual use unnecessarily. The jitter optimizer removes the assignments.

OTHER TIPS

Yes, it is still the right way to handle deterministic disposal. Using auto_handle is the C++/CLI equivalent of the C# using statement.

Using auto_handle (which uses RAII) allows you to get exception safety (i.e. the object is disposed even if an exception is thrown) without having to write any try-catch.

See also: C++/CLI Resource Management Confusion

There is, I think, a simpler way to do deterministic disposal of managed objects. Simply declare them without a ^, and you'll get the same type of behavior as you would by leaving the * off of an unmanaged C++ class: The constructor is called when the variable is initialized, and the 'destructor' (actually the Dispose method) is called when the variable goes out of scope.

When this is compiled, it turns into a try-catch-dispose if it's a local variable, or it adds a line to that class's Dispose method if it's a class field.

This is not drastically different from the auto_handle, which has a call to Dispose in it's destructor, but I think the syntax is easier to work with.

Examples:

C++/CLI:

public ref class Foo
{
    AutoResetEvent are;

public:
    Foo() : are(false)
    {
        this->are.Set(); // Use a '.' to access members.
    }

    void SomeMethod()
    {
        AutoResetEvent are2(false);
        are2.Set();
    }
};

Equivalent C#, from Reflector, so you can see what it's doing behind the scenes:

public class Foo : IDisposable
{
    // Fields
    private readonly AutoResetEvent modreq(IsByValue) are;

    // Methods
    public Foo()
    {
        AutoResetEvent modopt(IsConst) event2 = new AutoResetEvent(false);
        try
        {
            this.are = event2;
            base..ctor();
            this.are.Set();
        }
        fault
        {
            this.are.Dispose();
        }
    }

    public void ~Foo()
    {
    }

    public sealed override void Dispose()
    {
        this.Dispose(true);
        GC.SuppressFinalize(this);
    }

    [HandleProcessCorruptedStateExceptions]
    protected virtual void Dispose([MarshalAs(UnmanagedType.U1)] bool flag1)
    {
        if (flag1)
        {
            try
            {
                this.~Foo();
            }
            finally
            {
                this.are.Dispose();
            }
        }
        else
        {
            base.Finalize();
        }
    }

    public void SomeMethod()
    {
        AutoResetEvent are2 = null;
        AutoResetEvent modopt(IsConst) event2 = new AutoResetEvent(false);
        try
        {
            are2 = event2;
            are2.Set();
        }
        fault
        {
            are2.Dispose();
        }
        are2.Dispose();
    }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top