views:

101

answers:

7

Can compilers (for e.g. C#) automatically generate call to Dispose method on the object when it is set to null (of course, object should support Dispose method in first place). For example, if we write

cnSqlConnection = null;

and cnSqlConnection is an instance of type SqlConnection, can C# compiler inject Dispose method call right before updating reference to null?

Also, since framework classes do support scenario where Dispose method might get called multiple times, there would be no harm if the call is duplicated.

A: 
public static class MissingCompilerFeatures
{
    public static void SetToNullAndDispose(ref IDisposable obj)
    {
        if (obj != null) { obj.Dispose(); obj = null; }
    }
}
Andrey
+7  A: 

(a) A correctly-implemented object should perform the same cleanup logic in its finalizer. If you omit the call to Dispose, the finalizer logic will probably run anyway.

(b) Raymond Chen explains the complexity of auto-dispose here. In summary: it's only really safe to get a human programmer to call Dispose at the right point. If you take auto disposal to its logical conclusion then you end up with reference counting, which is what the CLR memory model sets out to avoid.

Tim Robinson
Precisely. There is no way to efficiently and correctly do this in general, unless a specialized syntax was introduced (e.g., C++'s stack semantics for reference variables). You could then argue that specialized syntax already exists (the `using` block).
Stephen Cleary
Besides, what would be the use of translating `con = null;` to `con.Dispose();`. It doesn't make programming easier (as `using` statements do), but would only serve programmers coming from old environments such as Visual Basic (and don't understand .NET).
Steven
+1, though I'd add that if the finaliser is only going to do stuff that would be triggered by the finalisers of field objects anyway, and there's no way they could have "escaped" and not also be subject to collection, then its reasonable to have Dispose() without having a finaliser.
Jon Hanna
A: 

Setting an object reference to null will mark the object as released for garbage collection, but it will not call Dispose explicitly. It If an object implements IDisposable, you should call Dispose() explicitly when you are done with the object, or wrap it in a using statement.

Here's some good information on IDisposable and garbage collection:

http://www.xtremedotnettalk.com/showthread.php?t=97910

Dave Swersky
No, setting **every** object reference to null will mark it as suitable for collection. The distinction is an important part of why having a compiler call Dispose() is a bad one.
Jon Hanna
+2  A: 

Techincally, yes, the compiler could do this but ideally, it wouldn't.

The reason is that you would run into issues with determining if there is anything else holding onto the reference. The compiler cannot infer who holds the reference at compile time (maybe through static analysis it could, but still, it's not guaranteed).

Now, it may be possible to do this at runtime, but that's still not ideal. It would require the equivalent of a GC every time a reference is set to null (a mark and sweep). Then for anything that is GCed, if there is an IDisposable implementation on it, call Dispose. This would drag the CLR into the mud, and make it perform horribly.

Of course, there's always reference counting (as nonnb mentioned in his comment on the question), but that's just going back to COM, and not ideal either. The complexities of reference counting are what gave birth to the GC aspect of the CLR in the first place.

Something to consider: If you have a situation where you don't know who actually has ownership of your IDisposable implementation, then it represents a design flaw in your system. If you have a function that works on such instances, it should either make the explicit declaration that it will Dispose of such instances, or it will leave that to the discretion of the caller (the latter being the preferred method).

casperOne
+1  A: 

Yes, the compiler could automatically generate the Dispose call. The real question is whether this a good idea or not. And the answer to that question is most definitely no.

Consider the following example.

void DoSomething(IDisposable disposable)
{
  DoSomethingElse(disposable);
  disposable = null;
}

In the example above how do you that DoSomethingElse does not hold its own separate reference to the disposable instance or that the caller of DoSomething does something similiar? That would really muck things up if the object got disposed while other pieces of code are assuming that the object is "live".

A more interesting question is why structs that IDisposable do have Dispose called automatically when their scope ends. It should be plainly obvious why doing this for reference types will not work, but what about structs? Consider the following complications.

  • Should you allow boxing or not?
  • Should you restrict them to being passed by reference only?
  • Should you allow assignments to the variable?

I am certain the list is not exhaustive, but you get the point. These problems are solvable, but is it really worth the effort? The compiler might have to place so many restrictions on them that their use cases become quite limited. It is an interesting subject though.

Regarding this subject you may find this blog interesting.

Brian Gideon
+4  A: 

That would be quite inconsistent, I give some examples:
Given class:

class A : IDisposable { public void Dispose() { } }

Example 1:

IDisposable a = new A();
IDisposable b = a;
a = null; // The object is still alive in b, should it really be disposed?

Example 2:

IDisposable a = new A();
IDisposable b = new A();
a = b; // a is not accessible anymore, but not set to null, 
       //shouldn't it be disposed here?

Example 3:

private void Foo()
{
    IDisposable a = new A();
    // a is not used any more, but not set to null, 
    //shouldn't it be disposed here?
}

In c# there is the using block that solves the problem you are heading at:

using (IDisposable a = new A())
{
    // Do stuff
}   // Here a.Dispose() is automatically called.
Albin Sunnanbo
+2  A: 

It can't, it is just an object reference. There could be many references to the same object, they would all point to a dead object if the compiler did this automatically. That's a guaranteed kaboom.

A core property of a garbage collector is that neither the compiler nor you can find out how many references exist to an object. Only the garbage collector can do that. And it already does, it automatically finalizes an object if all references are gone.

The point of IDisposable() is to not wait until that happens. Since the compiler cannot do this, nor can it be automated, it is up to you to make the promise that no other live references to the object exists. If you can't make that promise then you should not call Dispose() and leave it up to the collector. Standard in COM interop code for example.

Other automatic memory management schemes can do this. They use reference counting, common before garbage collectors became main-stream. Expensive and unreliable because it cannot deal with cycles. Microsoft funded a research project to add reference counting to the CLR. It was a big bust. GC got a lot more respect after that.

Hans Passant