1
votes

Is there a way I can cancel a dispose from within the Dispose method, in other words NOT dispose A when A.Dispose() is called.

Update I didn't explain well, 'cos I couldn't think of a way to put it nicely. What I have is a class that displays a progress bar (marquee) in a popup window that runs in a separate thread. When the progress popup is created, a controller-like disposable object is returned, so we can do something like this:

using (var progress = Dialogs.ShowProgress("Please wait..."))
{
    // do lots of stuff here
    progress.UpdateStatus("Please wait some more...");
    // do more stuff
}

when the using block exits, the progress window is closed and destroyed.

Now I have situations where things that happen within this block also create a progress window and instead of returning a new object and creating a new window, I wanted to return the object created by the first instance, and only destroy it once the first instance disposes.

Update Ok, what I did was to create a new IDisposable object with each Dialogs.ShowProgress() call, and attached the original controller object to it. These objects get disposed of whenever they're done, and the controller only gets disposed when it's the only object left to dispose.

5
Why in the world would you want to do that? Basically you're asking "I have this contract that says I must do X, but I don't want to - how do I get out of it?" - JerKimball
I smell framework abuse here. If the entire .NET framework doesn't have the need to cancel a Dispose() call do you think you really need to? - Sten Petrov
You are confusing the Dispose method, which is a convention (widely used across the .NET Framework, but not a language thing, just a convention), with a finalizer (which has a different syntax), and still you can't cancel a finalizer (which makes no sense)... the way to "cancel" Dispose is just... not calling Dispose. - Jcl
@Jcl: Actually there is a way to "cancel" a finalizer. When the GC categorizes a reference as "dead and needing finalization" it puts it in the finalizer queue, at which point it becomes "alive" again. If the finalizer causes the reference to the object to remain alive then you have a bona fide alive object again that the GC will not clean up until it is dead again. (And in fact, the object can mark itself as "treat me as though I was never finalized" if it wants the finalizer to run again the second time it is dead.) These techniques are for advanced players only. - Eric Lippert
@Jcl: You can do it with ordinary C#. Pooling strategies often use this technique. When the object is about to be released by the GC, the GC puts it on the finalizer queue and when the finalizer runs, it puts the object right back in the pool, making it alive, and tells the object "you still haven't been finalized" so that the next time it goes around this karmic cycle, it gets resurrected again. - Eric Lippert

5 Answers

4
votes

As others have said, it's a bad idea to implement IDisposable in a way that might not really dispose. Here's an alternate way to implement what you're trying to do:

interface IMaybeDisposable
{
    bool TryDispose(); //return false if dispose was canceled
}

You might use it like:

IMaybeDisposable maybe = //something
try
{
    // do something with maybe
}
finally
{
    if (maybe.TryDispose())
        // yay, it disposed!
    else
        // hm, it didn't dispose; do something else
}
1
votes

Sure. Just don't do whatever you would have done to dispose the object. You don't need to actively do anything special.

Be careful though; when someone disposes of an object they expect it to actually dispose of it's unmanaged resources, rather than ignoring you and holding into them anyway.

1
votes

The required post-condition for Dispose is that the client code should be able to abandon all references to the object without causing any necessary cleanup to go uncompleted. If that condition applies before Dispose is called, then the Dispose method may legitimately do nothing. If that condition does not apply, Dispose must make it do so, and may not legitimately return unless or until it does [note that Dispose need not actually perform the cleanup, if it sets in motion a chain of events via which the cleanup will occur at some later time]. Nothing in the contract for the Dispose interface allows it to sometimes leave objects in a state requiring further cleanup unless it has reason to believe that cleanup will occur via some other means.

1
votes

I would be careful about doing this. If you cancel disposing a method at the end of a using block, then you've lost the reference to that object and you'll never have a chance to have it dispose of its contents again, meaning that you could leak resources if you're not careful. In particular, I would only do this myself if the dispose method released references to large objects that I didn't want garbage collected right away for whatever reason. I wouldn't do this to skip releasing handles to files, database connections, network sockets, or anything else that can't be garbage collected.

As the commenters pointed out, I would try to refactor my code so that whatever problem I was trying to solve by canceling the dispose would be solved some other, less dangerous way.

If you're absolutely sure that you have an edge condition where you need to skip disposing something, here's one way to do it:

public void Dispose()
{
     if(condition)
     {
         // do disposing stuff
     }
}
0
votes

Based on your update, I would do something like this:

 public  void  DoStuff()
{
     DoPrepWork();
     using( var  controller =  TheMethodYouWroteThatShouldReturnAController())
    {
         DoStuffOverControllerLifetime();
    }
}