More fun with Dispose()



  • In the C# code to which I'll be adding some functionality, I just came across a passage that is essentially:

    Foo foo = new Foo()
    Bar bar;
    using (foo)
    {
        bar = foo.getBar();
    }
    

    // 20 or so lines of this and that

    Bletch bletch = foo.getBletch(); // Here I have a WTF??? moment.

    I need a stiff drink, ASAP.



  • Perhaps someone doesn't understand what Using does?  And you said adding functionality?  How is that happening when it seems this wouldn't even work to begin with?

    Ok maybe this is variable shadowing. The foo in the using statement is not the same foo declared above and used below.



  •  @KattMan said:

    Perhaps someone doesn't understand what Using does?  And you said adding functionality?  How is that happening when it seems this wouldn't even work to begin with?

    Ok maybe this is variable shadowing. The foo in the using statement is not the same foo declared above and used below.

    I suppose it could work, as long as Dispose() does not do anything that affects foo's ability to getBletch(). I'm not positive how the GC works in this instance, but I think the variable will still be in scope after the using block.  It's probably just a poor design of foo and/or a deficient understanding of how IDisposable is intended to be used.



  • For those of us not familiar with C#, what does the using keyword do? I'm not sure that I understand what the WTF is...



  • @ShatteredArm said:

    I suppose it could work, as long as Dispose() does not do anything that affects foo's ability to getBletch(). I'm not positive how the GC works in this instance, but I think the variable will still be in scope after the using block.  It's probably just a poor design of foo and/or a deficient understanding of how IDisposable is intended to be used.

    Yup, that's what I think, that this code does not fail always and hard because the access of "foo" outside of the using block did not stumble over anything nulled-out in the Displose method. But Lord Almighty what a cruddy way to code! You can't know that this is OK if you don't have detailed knowledge of how Foo is written.

    For non-C# folks, the "using" idiom is syntactic candy;

    using (foo)
    {
         //stuff
    }
    

    ...is equivalent to

    try
    {
         //stuff
    }
    finally
    {
        foo.Dispose();
    }
    

    The "Dispose" method is declared in the IDisposable interface; it is used to free up resources such as file handles and DB connections.



  • @ShatteredArm said:

    I suppose it could work, as long as Dispose() does not do anything that affects foo's ability to getBletch(). I'm not positive how the GC works in this instance, but I think the variable will still be in scope after the using block.  It's probably just a poor design of foo and/or a deficient understanding of how IDisposable is intended to be used.

    Have to remember there is another foo variable.

    One foo is decalred before the using.  Using usually is defined as using(Foo foo) so it creates another instance which it controls and disposes of properly.  Attempting to access the property of an object that has already had Dispose called on it gives an exception of "can not access a disposed object". 

    So my suspision is that we have two foos.  One declared above, then one declared in the using which within the scop of using shadow the prior declared foo, then after using the iner foo is disposed and out of scope and the later getBlech is called on the first outer foo.  Talk about unexpected results, this is bad all the way around.

    Also FYI I do believe even VB.Net has a using statement now.



  • @KattMan said:

    @ShatteredArm said:

    I suppose it could work, as long as Dispose() does not do anything that affects foo's ability to getBletch(). I'm not positive how the GC works in this instance, but I think the variable will still be in scope after the using block.  It's probably just a poor design of foo and/or a deficient understanding of how IDisposable is intended to be used.

    Have to remember there is another foo variable.

    One foo is decalred before the using.  Using usually is defined as using(Foo foo) so it creates another instance which it controls and disposes of properly.  Attempting to access the property of an object that has already had Dispose called on it gives an exception of "can not access a disposed object". 

    So my suspision is that we have two foos.  One declared above, then one declared in the using which within the scop of using shadow the prior declared foo, then after using the iner foo is disposed and out of scope and the later getBlech is called on the first outer foo.  Talk about unexpected results, this is bad all the way around.

    Also FYI I do believe even VB.Net has a using statement now.

     

    You can use the object after it is disposed.  Perhaps you're confusing the dispose() method with the GC disposing of it?



  • @KattMan said:

    One foo is decalred before the using.  Using usually is defined as using(Foo foo) so it creates another instance which it controls and disposes of properly.  Attempting to access the property of an object that has already had Dispose called on it gives an exception of "can not access a disposed object".

    Interesting. Java won't let you do this; the variables have to be declared in the try-with-resources and they cannot shadow a variable in the outer scope. Because of this, I don't think it's possible to access an object once it's been "disposed" of.



  • @KattMan said:

    ...So my suspision is that we have two foos...

    Gosh, KattMan, I don't think so. This would absolutely be true if the expression were using(Foo foo), but I'm betting that a simple using (foo) references the variable declared above, in the enclosing block. Otherwise, wouldn't the compiler complain about using an uninitialized variable?

    This probably was not clear because of the compressed presentation, but in the WTF code, there was no "new" operator within the "using" clause -- just the naked variable.



  • @KattMan said:

    Also FYI I do believe even VB.Net has a using statement now.
    Yes, if by "now" you mean "seven years ago".



  • @ShatteredArm said:

    You can use the object after it is disposed.  Perhaps you're confusing the dispose() method with the GC disposing of it?

    Nope not confusing it at all.  The GC can dispose of it at any point after you call dispose, maybe immediatly, maybe some time later, thing is you have no control over that.  Disposing of an object then attempting to access a property or method of that object can very well give you the error mentioned, if you couldn't then why even have the exception designed to catch this very thing?  I have seen it happen, had to fix the problems caused by someone losing track of what was disposed and what wasn't while writing thier code.  If you call dispose, you should be done with it, not trying to access another property or method on that instance, that's just asking for trouble.

    As far as VB "now" having it, that was for the poster that asked for those not familiar with c# to explian using.



  • @KattMan said:

    Have to remember there is another foo variable.

    One foo is decalred before the using.

    Not according to the posted code.  But I suppose if you're working with some theoretical code that you just thought up...

    @KattMan said:

    Using usually is defined as using(Foo foo) so it creates another instance which it controls and disposes of properly. 

    Except that code wouldn't compile, since C# won't let you hide a variable like that.@KattMan said:
    Attempting to access the property of an object that has already had Dispose called on it gives an exception of "can not access a disposed object". 
    It can, depending on how the object is written, it is not guaranteed.


  • @KattMan said:

    The GC can dispose of it at any point after you call dispose, maybe immediatly, maybe some time later, thing is you have no control over that.

    Actually the GC can't collect it until all references to it are gone.  Which is never immediately after calling dispose.



  • @KattMan said:

    @ShatteredArm said:

    You can use the object after it is disposed.  Perhaps you're confusing the dispose() method with the GC disposing of it?

    Nope not confusing it at all.  The GC can dispose of it at any point after you call dispose, maybe immediatly, maybe some time later, thing is you have no control over that.  Disposing of an object then attempting to access a property or method of that object can very well give you the error mentioned, if you couldn't then why even have the exception designed to catch this very thing?  I have seen it happen, had to fix the problems caused by someone losing track of what was disposed and what wasn't while writing thier code.  If you call dispose, you should be done with it, not trying to access another property or method on that instance, that's just asking for trouble.

    As far as VB "now" having it, that was for the poster that asked for those not familiar with c# to explian using.

    class MyAwesomeDisposableClass : IDisposable {
        private bool disposed = false;
        private IDisposable SomeOtherClass = new SomeOtherClass();
        private IntPtr SomeHandle = PInvoke.GetAHandle();
    
        public void Dispose() {
            Dispose(true);
        }
    
        private void Dispose(bool manual) {
            if(disposed) return;
            
            disposed = true;
    
            if(manual) {
                SomeOtherClass.Dispose();
            }
    
            if(SomeHandle != IntPtr.Zero) {
                PInvoke.FreeMyHandle(SomeHandle);
            }
    
            GC.SuppessFinalization(this);
        }
        
        private ~MyAwesomeDisposableClass() {
            Dispose(false); //if this runs, someone forgot to use using did something wrong
        }
       
        public void DoWork() {
            if(this.disposed) throw new ObjectDisposedException();
            //...
        }
    }

    This is (a rough, from memory, probably typo laden implementation of) how you're supposed to implement IDisposable. You can do it in other ways, but that would be incorrect. This is where ObjectDisposedException comes from, and this is where our native resources are dealt with.

    If Dispose() is called manually, then we also dispose other managed objects we own. If the finalizer is called, we do not touch our managed objects, because they may have been collected already, so we just free our native resources, and move on.

    Dispose() is allowed to do nothing, if there's nothing to do, but in that case you shouldn't be implementing IDisposable (unless required by another interface or inheritance or whatever)



  •  it's the same foo variable since it's declared outside of the using block.  but I believe that because of this, foo.method will still work if the method is static.  but if it uses any parameters, those would now be set to null.

     though he has no idea how to use a using statement.  I wonder if he's a vb programmer that ment to use a with statement?



  •  @TomTom said:

     it's the same foo variable since it's declared outside of the using block.  but I believe that because of this, foo.method will still work if the method is static.  but if it uses any parameters, those would now be set to null.

    Where the hell do you people get this stuff?



  • @ShatteredArm said:

     @TomTom said:

     it's the same foo variable since it's declared outside of the using block.  but I believe that because of this, foo.method will still work if the method is static.  but if it uses any parameters, those would now be set to null.

    Where the hell do you people get this stuff?

    Good question.  It's weird what people will say.  But at least his first sentence was correct.

  • SockDev

    @rstinejr said:

    @ShatteredArm said:

    I suppose it could work, as long as Dispose() does not do anything that affects foo's ability to getBletch(). I'm not positive how the GC works in this instance, but I think the variable will still be in scope after the using block.  It's probably just a poor design of foo and/or a deficient understanding of how IDisposable is intended to be used.

    Yup, that's what I think, that this code does not fail always and hard because the access of "foo" outside of the using block did not stumble over anything nulled-out in the Displose method. But Lord Almighty what a cruddy way to code! You can't know that this is OK if you don't have detailed knowledge of how Foo is written.

    For non-C# folks, the "using" idiom is syntactic candy;

    using (foo)
    {
         //stuff
    }
    

    ...is equivalent to

    try
    {
         //stuff
    }
    finally
    {
        foo.Dispose();
    }
    

    The "Dispose" method is declared in the IDisposable interface; it is used to free up resources such as file handles and DB connections.

    Close, but not quite - there's a missing null check.

    try
    {
         //stuff
    }
    finally
    {
        if (foo != null)
            foo.Dispose();
    }
    

     

     



  • @Sutherlands said:

    @ShatteredArm said:

     @TomTom said:

     it's the same foo variable since it's declared outside of the using block.  but I believe that because of this, foo.method will still work if the method is static.  but if it uses any parameters, those would now be set to null.

    Where the hell do you people get this stuff?

    Good question.  It's weird what people will say.  But at least his first sentence was correct.
     

    Right.  That nonsense about the static method and null parameters is what I was referring to.  I'm honestly really curious where people get these ideas, and at the same time, very concerned that these people might actually be paid for this "knowledge."



  • @KattMan said:

    @ShatteredArm said:

    You can use the object after it is disposed.  Perhaps you're confusing the dispose() method with the GC disposing of it?

    Nope not confusing it at all.  The GC can dispose of it at any point after you call dispose, maybe immediatly, maybe some time later, thing is you have no control over that.  Disposing of an object then attempting to access a property or method of that object can very well give you the error mentioned, if you couldn't then why even have the exception designed to catch this very thing?  I have seen it happen, had to fix the problems caused by someone losing track of what was disposed and what wasn't while writing thier code.  If you call dispose, you should be done with it, not trying to access another property or method on that instance, that's just asking for trouble.

    As far as VB "now" having it, that was for the poster that asked for those not familiar with c# to explian using.

    No. First of all, C# doesn't allow function level shadowing. 2. The only thing that Dispose() does, or what it was designed to do is release unmanaged resources. 3. The collector will not delete an object until after its memory becomes unreachable.


Log in to reply
 

Looks like your connection to What the Daily WTF? was lost, please wait while we try to reconnect.