正确实现 IDisposable

在我的课堂上,我实现 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;
}
}

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

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

参考资料: CA1063: 正确实现 IDisposable

我已经看完了这一页,但是恐怕我真的不明白这里需要做些什么。

如果有人能够更通俗地解释问题是什么和/或者 IDisposable应该如何实现,那将会非常有帮助!

141582 次浏览

IDisposable的存在为您提供了一种清理 无人管理资源的方法,这些资源不会被垃圾收集器自动清理。

您正在“清理”的所有资源都是托管资源,因此您的 Dispose方法没有完成任何工作。您的类根本不应该实现 IDisposable。垃圾收集器将自己处理所有这些字段。

首先,您不需要“清理”stringint——它们将由垃圾收集器自动处理。在 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;
}
}

你需要像这样使用 一次性模式:

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);
}

这将是正确的实现,尽管在您发布的代码中我没有看到任何需要处置的内容。只有在下列情况下才需要实现 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.
}
}

您不需要将 User类设置为 IDisposable,因为类 不会获得包含任何非托管资源(文件、数据库连接等)。通常,我们将类标记为 如果它们至少有一个 IDisposable字段或/和属性,则为 IDisposable。 在实现 IDisposable时,最好按照微软的典型方案:

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);
}
}

只要您需要确定性(确认的)垃圾收集,就可以实现。

class Users : IDisposable
{
~Users()
{
Dispose(false);
}


public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
// This method will remove current object from garbage collector's queue
// and stop calling finilize method twice
}


public void Dispose(bool disposer)
{
if (disposer)
{
// dispose the managed objects
}
// dispose the unmanaged objects
}
}

在创建和使用 Users 类时,使用“ using”块来避免显式调用释放方法:

using (Users _user = new Users())
{
// do user related work
}

结束使用块创建的用户对象将通过隐式调用释放方法释放。

下面的示例显示了实现 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.
}
}

我看到了很多 Microsoft Dispose 模式的例子,它实际上是一个反模式。正如许多人指出的那样,问题中的代码根本不需要 IDisposable。但是如果你要实现它,请不要使用微软模式。更好的答案是遵循本文中的建议:

Https://www.codeproject.com/articles/29534/idisposable-what-your-mother-never-told-you-about

唯一可能有帮助的是抑制代码分析警告... https://learn.microsoft.com/en-us/visualstudio/code-quality/in-source-suppression-overview?view=vs-2017