简体   繁体   中英

How is memory handled when we return from a method in a `using` block?

Consider the following code:

public Bar GetBar()
{
    using(var foo = new Foo())
    {
        return foo.Bar;
    }
}

Is this leaky, or is foo.Dispose() called? Is it possible that the return value is invalid as a result of foo being disposed?

The answers here are close but not complete and are inconsistent.

Is this leaky, or is foo.Dispose() called?

Dispose is called. The using block gets converted to a try / finally block in which foo is disposed of in the finally section. finally will be called after the try completes (whether by exception or naturally) and before returning to the caller. So Dispose will be called in virtually every situation (barring very serious exceptions such as out of memory, thread aborts, etc.)

Is it possible that the return value is invalid as a result of foo being disposed?

Sure, if Dispose does something to invalidate the object that Bar references, then yes, it certainly could be returning an "invalid" reference. But that would have to be explicit within Foo . Disposing of an object does not automatically dispose of all properties.

Is this leaky

No. This is good coding practice for almost any object implementing IDisposable because it ensures that the dispose method is called as soon as the variable being 'used' goes out of scope (eg. the code leaves the using block). There are a few exceptions (WCF clients, custom code that doesn't follow good IDisposable practices), but it is a good idea to wrap ANY IDisposable in a using block unless you have a specific reason not to.

Is it possible that the return value is invalid as a result of foo being disposed

This depends on what the Dispose method in Foo does. For example, if you try to invoke a SQLCommand that references a SQLConnection that has already been closed/disposed, you are going to get an exception. Lets look at an example that makes this behavior obvious.

public class Foo : IDisposable
{
    public Foo()
    {
        ComplexType = new ComplexType();
    }

    public ComplexType ComplexType { get; set; }

    public void Dispose()
    {
        ComplexType = null;
        GC.Collect();
    }
}

Now this piece of code that accesses our Foo:

    static void Main(string[] args)
    {
        Foo foo;
        ComplexType complexType;

        using (var newFoo = new Foo())
        {
            foo = newFoo;
            complexType = newFoo.ComplexType;
        }

        Console.WriteLine(complexType.SomeProperty); // This works :)
        Console.WriteLine(foo.ComplexType.SomeProperty); // Throws an exception because ComplexType is NULL

        Console.ReadKey();

    }

Weird right? The reason this happens is because in the Foo constructor we create a new ComplexType and store it at a memory address. The Foo.ComplexType property contains a reference to the memory address. When we call dispose, we set the reference to null, but the actual object is not garbage collected because we have other references to it in our calling code, so we can't access it through the foo.ComplexType property anymore, but it is still accessible via the complexType variable. Note as well that foo is NOT null even though it has been assigned to an object that has been disposed. Because a reference still exists to our Foo instance, despite being outside of the using block, it is still alive since it can't be collected while that reference exists.

Now, if the Dispose method changed SomeProperty, it is possible (depending on how it was changed), that the change COULD propagate out and invalidate results.

I guess the moral of the story is that you could experience (or create) all sorts of weird behavior if you start playing around with objects that have been disposed, but that depends on what the disposed objects are doing during disposal. I wouldn't recommend it as a practice since most objects aren't intended to be used after disposal. Perform your "work" as an atomic unit inside the using block, and then let the object die.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM