Closures, Deferred Execution and IDisposable

  • Thread starter Lasse Vågsæther Karlsen
  • Start date
L

Lasse Vågsæther Karlsen

Caddzooks said:
Can someone spot the problem here:

public class Form1 : Form
{
public Form1()
{
using( MyLockObject lockobj = new MyLockObject() )
{
Application.Idle += delegate( object sender, EventArgs e )
{
lockobj.Kaboom();
};
}
}
}

Because the delegate assigned to the Idle event references an object (MyLockObject) that will already have been disposed when the event fires, it will be attempting to use a disposed object.

This itself isn't the problem, as I know that if I want to reference that object from code that runs after the fact, I can't dispose the object in the ctor.

The question is, why doesn't the compiler catch it, and help the programmer avoid a very hideous bug?

Well, in this particular case, the compiler would have to have lots of
assumptions about your code in order to warn you, and knowing these
assumptions might not be possible at all times.

Assumptions like:

- the Idle event is probably not going to be called in the constructor,
but much later (if you think about it, this is not an assumption about
the "Idle" event, but rather all events)
- the object is indeed unsafe to use after being Disposed (which is a
logical assumptions, but is no guarantee), in the manner it is used. For
instance, what if the method being called internally calls "IsDisposed"
and does nothing or something else that wouldn't crash in this case?

I don't think the C# compiler, without having a much more advanced
analysis engine, would be able to detect this reliable enough to warrant
this feature.

Resource tracking and proper usage is not easy. It's not *very* hard
either, but you need to know what you're doing, and in this case you're
yanking away the branch that the event is sitting on. The proper way
would be to unsubscribe the event before disposing of the object.

And I know what you asked, I'm just saying that I don't think it would
be possible to write a C# compiler that would detect all these nuances
and handle them with any guarantee as of the results.
 
J

Jeroen Mostert

Caddzooks said:
Can someone spot the problem here:

public class Form1 : Form
{
public Form1()
{
using( MyLockObject lockobj = new MyLockObject() )
{
Application.Idle += delegate( object sender, EventArgs e )
{
lockobj.Kaboom();
};
}
}
}

Because the delegate assigned to the Idle event references an object
(MyLockObject) that will already have been disposed when the event fires,
it will be attempting to use a disposed object.

This itself isn't the problem, as I know that if I want to reference that
object from code that runs after the fact, I can't dispose the object in
the ctor.

The question is, why doesn't the compiler catch it, and help the
programmer avoid a very hideous bug?
Because the general problem of detecting whether code contains an error is
unsolvable.

But more to the point, the very notion of "a disposed object" is outside the
realm of the language proper. As far the compiler is concerned, "using" is
just shorthand for a finally block with a .Dispose() call. It knows nothing
about what .Dispose() does.

Indeed, it is *not* an error (and potentially useful) to keep a reference to
a disposed object, and there's nothing in the language that makes it illegal
to call methods of an object on which .Dispose() has been called. The object
actually needs to check for this itself (and throw an
ObjectDisposedException if it's so inclined) and the caller needs to know
about this (or err on the safe side).

Garbage collection and finalization are under the survey of the compiler and
the runtime. IDispose and its implementation are not. Conceivably, a tool
like FxCop could be outfitted with a rule to detect this problem, like it
detects many other bad practices and potential problems that the compiler
will not warn you about.
 
C

Caddzooks

Can someone spot the problem here:

public class Form1 : Form
{
public Form1()
{
using( MyLockObject lockobj = new MyLockObject() )
{
Application.Idle += delegate( object sender, EventArgs e )
{
lockobj.Kaboom();
};
}
}
}

Because the delegate assigned to the Idle event references an object (MyLockObject) that will already have been disposed when the event fires, it will be attempting to use a disposed object.

This itself isn't the problem, as I know that if I want to reference that object from code that runs after the fact, I can't dispose the object in the ctor.

The question is, why doesn't the compiler catch it, and help the programmer avoid a very hideous bug?

--
caddzooks
 
L

Lasse Vågsæther Karlsen

Caddzooks said:
I agree - its probably beyond the scope of the compiler without more advanced constraints or some other mechansim that allows the programmer to make an assertion about the usable life of an object.

However this particular example was simplified. The object that was referenced from within the delegate could have been anything, and could be owned by some other object, or returned by a property reference or method call, and the programmer writing the delgate may not have any idea about, or be responsible for managing its lifetime.

So I guess what it means is that since one cannot make assumptions about when a delegate may run, they cannot make assumptions about whether any objects visible from its closure are still alive when the delegate runs.

If you, in a closure, access objects that are created outside of the
closure, or outside of the method that created the closure, then you
need to take measures to handle this, yes.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top