正确实现IDisposable

Ort*_*und 132 .net c# garbage-collection memory-leaks memory-management

在我的类中,我实现IDisposable如下:

public class User : IDisposable
{
    public int id { get; protected set; }
    public string name { get; protected set; }
    public string pass { get; protected set; }

    public User(int UserID)
    {
        id = UserID;
    }
    public User(string Username, string Password)
    {
        name = Username;
        pass = Password;
    }

    // Other functions go here...

    public void Dispose()
    {
        // Clear all property values that maybe have been set
        // when the class was instantiated
        id = 0;
        name = String.Empty;
        pass = String.Empty;
    }
}
Run Code Online (Sandbox Code Playgroud)

在VS2012中,我的代码分析说要正确实现IDisposable,但我不确定我在这里做错了什么.
具体文字如下:

CA1063正确实现IDisposable在"用户"上提供Dispose(bool)的可覆盖实现,或将类型标记为已密封.对Dispose(false)的调用应该只清理本机资源.对Dispose(true)的调用应该清理托管和本机资源.stman User.cs 10

供参考:CA1063:正确实施IDisposable

我已经阅读了这个页面,但是我担心我真的不明白这里需要做些什么.

如果任何人都可以用更多的术语来解释问题是什么和/或IDisposable应该如何实现,这将真的有帮助!

Dan*_*ann 104

这将是正确的实现,虽然我没有看到您需要在您发布的代码中处置任何内容.您只需要在以下时间实施IDisposable:

  1. 您有非托管资源
  2. 你正在坚持引用本身就是一次性的东西.

您发布的代码中没有任何内容需要处理.

public class User : IDisposable
{
    public int id { get; protected set; }
    public string name { get; protected set; }
    public string pass { get; protected set; }

    public User(int userID)
    {
        id = userID;
    }
    public User(string Username, string Password)
    {
        name = Username;
        pass = Password;
    }

    // Other functions go here...

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    protected virtual void Dispose(bool disposing)
    {
        if (disposing) 
        {
            // free managed resources
        }
        // free native resources if there are any.
    }

}
Run Code Online (Sandbox Code Playgroud)

  • @Ortund你误会了.当类实现IDisposable*时,最好使用`using`块*.如果您不需要一个类是一次性的,请不要实现它.它没有任何意义. (59认同)
  • @DanielMann尽管如此,`using`块的语义确实倾向于超越`IDisposable`接口.我想有一些"IDisposable"的滥用仅仅是为了确定范围. (5认同)
  • 在实现中没有终结器调用`GC.SuppressFinalize(this);`是没有意义的.正如@mariozski所指出的那样,终结器有助于_ensure_,如果在`using`块中没有使用该类,则会调用`Dispose`. (4认同)
  • 当我开始用C#编写代码时,我被告知最好在可能的情况下使用`using(){}`,但是要做到这一点,您需要实现IDisposable,因此,总的来说,我更喜欢通过uses访问类,尤其是 如果我只需要一两个函数中的类 (2认同)

D S*_*ley 55

首先,你不需要"清理" strings和int- 它们将由垃圾收集器自动处理.唯一需要清理的Dispose是非托管资源或实施的托管资源IDisposable.

但是,假设这只是一个学习练习,推荐的实施方法IDisposable是添加"安全捕获"以确保任何资源不会被处置两次:

public void Dispose()
{
    Dispose(true);

    // Use SupressFinalize in case a subclass 
    // of this type implements a finalizer.
    GC.SuppressFinalize(this);   
}
protected virtual void Dispose(bool disposing)
{
    if (!_disposed)
    {
        if (disposing) 
        {
            // Clear all property values that maybe have been set
            // when the class was instantiated
            id = 0;
            name = String.Empty;
            pass = String.Empty;
        }

        // Indicate that the instance has been disposed.
        _disposed = true;   
    }
}
Run Code Online (Sandbox Code Playgroud)

  • +1,有一个标志,以确保清理代码只执行一次是方式,比将属性设置为null或更好(特别是因为它干扰`readonly`语义) (3认同)
  • +1 用于使用用户代码(即使它会自动清理)以明确那里的内容。此外,因为他不是一个咸水手,而是因为他在学习时犯了一个小错误而责备他,就像这里的许多其他人一样。 (3认同)

Cha*_*thJ 39

以下示例显示了实现IDisposable接口的一般最佳实践.参考

请记住,只有在类中有非托管资源时才需要析构函数(终结器).如果添加析构函数,则应该在Dispose中禁止Finalization,否则会导致对象驻留在内存中两个垃圾循环(注意:阅读Finalization的工作原理).下面的例子详细说明了上述内容

public class DisposeExample
{
    // A base class that implements IDisposable. 
    // By implementing IDisposable, you are announcing that 
    // instances of this type allocate scarce resources. 
    public class MyResource: IDisposable
    {
        // Pointer to an external unmanaged resource. 
        private IntPtr handle;
        // Other managed resource this class uses. 
        private Component component = new Component();
        // Track whether Dispose has been called. 
        private bool disposed = false;

        // The class constructor. 
        public MyResource(IntPtr handle)
        {
            this.handle = handle;
        }

        // Implement IDisposable. 
        // Do not make this method virtual. 
        // A derived class should not be able to override this method. 
        public void Dispose()
        {
            Dispose(true);
            // This object will be cleaned up by the Dispose method. 
            // Therefore, you should call GC.SupressFinalize to 
            // take this object off the finalization queue 
            // and prevent finalization code for this object 
            // from executing a second time.
            GC.SuppressFinalize(this);
        }

        // Dispose(bool disposing) executes in two distinct scenarios. 
        // If disposing equals true, the method has been called directly 
        // or indirectly by a user's code. Managed and unmanaged resources 
        // can be disposed. 
        // If disposing equals false, the method has been called by the 
        // runtime from inside the finalizer and you should not reference 
        // other objects. Only unmanaged resources can be disposed. 
        protected virtual void Dispose(bool disposing)
        {
            // Check to see if Dispose has already been called. 
            if(!this.disposed)
            {
                // If disposing equals true, dispose all managed 
                // and unmanaged resources. 
                if(disposing)
                {
                    // Dispose managed resources.
                    component.Dispose();
                }

                // Call the appropriate methods to clean up 
                // unmanaged resources here. 
                // If disposing is false, 
                // only the following code is executed.
                CloseHandle(handle);
                handle = IntPtr.Zero;

                // Note disposing has been done.
                disposed = true;

            }
        }

        // Use interop to call the method necessary 
        // to clean up the unmanaged resource.
        [System.Runtime.InteropServices.DllImport("Kernel32")]
        private extern static Boolean CloseHandle(IntPtr handle);

        // Use C# destructor syntax for finalization code. 
        // This destructor will run only if the Dispose method 
        // does not get called. 
        // It gives your base class the opportunity to finalize. 
        // Do not provide destructors in types derived from this class.
        ~MyResource()
        {
            // Do not re-create Dispose clean-up code here. 
            // Calling Dispose(false) is optimal in terms of 
            // readability and maintainability.
            Dispose(false);
        }
    }
    public static void Main()
    {
        // Insert code here to create 
        // and use the MyResource object.
    }
}
Run Code Online (Sandbox Code Playgroud)


Ser*_*rvy 14

IDisposable存在以提供一种方法来清理垃圾收集器不会自动清理的非托管资源.

您正在"清理"的所有资源都是托管资源,因此您的Dispose方法无需任何操作.你的班级根本不应该实施IDisposable.垃圾收集器将自行处理所有这些字段.

  • 不完全正确,Dispose方法还允许您处理"实现IDisposable的托管资源" (4认同)

Bel*_*gix 13

你需要像这样使用Disposable Pattern:

private bool _disposed = false;

protected virtual void Dispose(bool disposing)
{
    if (!_disposed)
    {
        if (disposing)
        {
            // Dispose any managed objects
            // ...
        }

        // Now disposed of any unmanaged objects
        // ...

        _disposed = true;
    }
}

public void Dispose()
{
    Dispose(true);
    GC.SuppressFinalize(this);  
}

// Destructor
~YourClassName()
{
    Dispose(false);
}
Run Code Online (Sandbox Code Playgroud)

  • @rdhs不,我们不是.MSDN声明****这是一种模式"Dispose Pattern" - https://msdn.microsoft.com/en-us/library/b1yfkh5e(v=vs.110).aspx所以在向下投票之前可能是谷歌小? (3认同)
  • @dotnetguy:在gc运行时调用对象析构函数.所以不可能两次打电话.请参阅此处:https://msdn.microsoft.com/en-us/library/ms244737.aspx (2认同)
  • 微软和你的帖子都没有明确说明为什么这种模式应该是这样的。通常,它甚至不是样板,它只是多余的 - 被 [`SafeHandle`](https://docs.microsoft.com/en-us/dotnet/api/system.runtime.interopservices.safehandle?view=netframework-4.7 取代.2)(和子类型)。在管理资源的情况下,实施适当处置变得更加简单;您可以将代码精简为 `void Dispose()` 方法的简单实现。 (2认同)

Dmi*_*nko 9

由于类没有获取任何非托管资源(文件,数据库连接等),因此您无需执行User类.通常,我们将类标记为 至少具有一个字段或/和属性.在实施时,最好根据微软的典型方案:IDisposableIDisposableIDisposableIDisposable

public class User: IDisposable {
  ...
  protected virtual void Dispose(Boolean disposing) {
    if (disposing) {
      // There's no need to set zero empty values to fields 
      // id = 0;
      // name = String.Empty;
      // pass = String.Empty;

      //TODO: free your true resources here (usually IDisposable fields)
    }
  }

  public void Dispose() {
    Dispose(true);

    GC.SuppressFinalize(this);
  } 
}
Run Code Online (Sandbox Code Playgroud)