Should “Dispose” only be used for types containing unmanaged resources?

后端 未结 15 1615
执笔经年
执笔经年 2020-12-05 01:41

I was having a discussion with a colleague recently about the value of Dispose and types that implement IDisposable.

I think there is value

15条回答
  •  南方客
    南方客 (楼主)
    2020-12-05 02:09

    everything ultimately is an unmanaged resource.

    Not true. Everything except memory used by CLR objects which is managed (allocated and freed) only by the framework.

    Implementing IDisposable and calling Dispose on an object that does not hold on to any unmanaged resources (directly or indirectly via dependent objects) is pointless. It does not make freeing that object deterministic because you can't directly free object's CLR memory on your own as it is always only GC that does that. Object being a reference type because value types, when used directly at a method level, are allocated/freed by stack operations.

    Now, everyone claims to be right in their answers. Let me prove mine. According to documentation:

    Object.Finalize Method allows an object to try to free resources and perform other cleanup operations before it is reclaimed by garbage collection.

    In other words object's CLR memory is released just after Object.Finalize() is called. [note: it is possible to explicitly skip this call if needed]

    Here is a disposable class with no unmanaged resources:

    internal class Class1 : IDisposable
    {
        public Class1()
        {
            Console.WriteLine("Construct");
        }
    
        public void Dispose()
        {
            Console.WriteLine("Dispose");
        }
    
        ~Class1()
        {
            Console.WriteLine("Destruct");
        }
    }
    

    Note that destructor implicitly calls every Finalize in the inheritance chain down to Object.Finalize()

    And here is the Main method of a console app:

    static void Main(string[] args)
    {
        for (int i = 0; i < 10; i++)
        {
            Class1 obj = new Class1();
            obj.Dispose();
        }
    
        Console.ReadKey();
    }
    

    If calling Dispose was a way to free a managed object in a deterministic way, every "Dispose" would be immediately followed by a "Destruct", right? See for yourself what happens. It is most interesting to run this app from a command line window.

    Note: There is a way to force GC to collect all objects which are pending finalization in the current app domain but no for a single specific object. Nevertheless you do not need to call Dispose to have an object in the finalization queue. It is strongly discouraged to force collection as it will likely hurt overall application performance.

    EDIT

    There is one exception - state management. Dispose can handle state change if your object happens to manage an outside state. Even if state is not an unmanaged object it is very convenient to use it like one because of special treatment IDisposable has. Example would be a security context or impersonation context.

    using (WindowsImpersonationContext context = SomeUserIdentity.Impersonate()))
    {
        // do something as SomeUser
    }
    
    // back to your user
    

    It is not the best example because WindowsImpersonationContext uses system handle internally but you get the picture.

    Bottom line is that when implementing IDisposable you need to have (or plan to have) something meaningful to do in the Dispose method. Otherwise it's just a waste of time. IDisposable does not change how your object is managed by GC.

提交回复
热议问题