Dealing with .NET IDisposable objects

后端 未结 12 766
自闭症患者
自闭症患者 2021-01-30 13:14

I work in C#, and I\'ve been pretty lax about using using blocks to declare objects that implement IDisposable, which you\'re apparently always suppose

相关标签:
12条回答
  • 2021-01-30 13:48

    I use using blocks primarily for this scenario:

    I'm consuming some external object (usually an IDisposable wrapped COM object in my case). The state of the object itself may cause it to throw an exception or how it affects my code may cause me to throw an exception, and perhaps in many different places. In general, I trust no code outside of my current method to behave itself.

    For the sake of argument, lets say I have 11 exit points to my method, 10 of which are inside this using block and 1 after it (which can be typical in some library code I've written).

    Since the object is automatically disposed of when exiting the using block, I don't need to have 10 different .Dispose() calls--it just happens. This results in cleaner code, since it is now less cluttered with dispose calls (~10 fewer lines of code in this case).

    There is also less risk of introducing IDisposable leak bugs (which can be time consuming to find) by somebody altering the code after me if they forget to call dispose, because it isn't necessary with a using block.

    0 讨论(0)
  • 2021-01-30 13:49

    Like Fxcop (to which they're related), the code analysis tools in VS (if you have one of the higher-up editions) will find these cases too.

    0 讨论(0)
  • 2021-01-30 13:59

    This is why (IMHO) C++'s RAII is superior to .NET's using statement.

    A lot of people said that IDisposable is only for un-managed resources, this is only true depending on how you define "resource". You can have a Read/Write lock implementing IDisposable and then the "resource" is the conceptual access to the code block. You can have an object that changes the cursor to hour-glass in the constructor and back to the previously saved value in IDispose and then the "resource" is the changed cursor. I would say that you use IDisposable when you want deterministic action to take place when leaving the scope no matter how the scope is left, but I have to admit that it's far less catchy than saying "it's for managing un-managed resource management".

    See also the question about why there's no RAII in .NET.

    0 讨论(0)
  • 2021-01-30 14:00

    I don't really have anything to add to the general use of Using blocks but just wanted to add an exception to the rule:

    Any object that implements IDisposable apparently should not throw an exception during its Dispose() method. This worked perfectly until WCF (there may be others), and it's now possible that an exception is thrown by a WCF channel during Dispose(). If this happens when it's used in a Using block, this causes issues, and requires the implementation of exception handling. This obviously requires more knowledge of the inner workings, which is why Microsoft now recommends not using WCF channels in Using blocks (sorry could not find link, but plenty other results in Google), even though it implements IDisposable.. Just to make things more complicated!

    0 讨论(0)
  • 2021-01-30 14:00

    According to this link the CodeRush add-in will detect and flag when local IDisposable variables aren't cleaned up, in real-time, as you type.

    Could meet you halfway on your quest.

    0 讨论(0)
  • 2021-01-30 14:05

    I'm not getting the point of your question. Thanks to the garbage collector, memory leaks are close to impossible to occur. However, you need some robust logic.

    I use to create IDisposable classes like this:

    public MyClass: IDisposable
    {
    
        private bool _disposed = false;
    
        //Destructor
        ~MyClass()
        { Dispose(false); }
    
        public void Dispose()
        { Dispose(true); }
    
        private void Dispose(bool disposing)
        {
            if (_disposed) return;
            GC.SuppressFinalize(this);
    
            /* actions to always perform */
    
            if (disposing) { /* actions to be performed when Dispose() is called */ }
    
            _disposed=true;
    }
    

    Now, even if you miss to use using statement, the object will be eventually garbage-collected and proper destruction logic is executed. You may stop threads, end connections, save data, whatever you need (in this example, I unsubscribe from a remote service and perform a remote delete call if needed)

    [Edit] obviously, calling Dispose as soon as possible helps application performance, and is a good practice. But, thanks to my example, if you forget to call Dispose it will be eventually called and the object is cleaned up.

    0 讨论(0)
提交回复
热议问题