在C#中完成/处理模式
C#2008
我一直在研究这个问题一段时间,而且我仍然对一些问题感到困惑。 我的问题如下
我知道如果你正在处理非托管资源,你只需要一个终结器。 但是,如果您使用调用非托管资源的托管资源,您是否仍需要实现终结器?
但是,如果您直接或间接开发一个不使用任何非托管资源的类,是否可以实现IDisposable
以便您的类的客户端可以使用“使用语句”?
实施IDisposable只是为了让您的班级的客户可以使用using语句是否可以接受?
using(myClass objClass = new myClass())
{
// Do stuff here
}
我在下面开发了这个简单的代码来演示Finalize / dispose模式:
public class NoGateway : IDisposable
{
private WebClient wc = null;
public NoGateway()
{
wc = new WebClient();
wc.DownloadStringCompleted += wc_DownloadStringCompleted;
}
// Start the Async call to find if NoGateway is true or false
public void NoGatewayStatus()
{
// Start the Async's download
// Do other work here
wc.DownloadStringAsync(new Uri(www.xxxx.xxx));
}
private void wc_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
{
// Do work here
}
// Dispose of the NoGateway object
public void Dispose()
{
wc.DownloadStringCompleted -= wc_DownloadStringCompleted;
wc.Dispose();
GC.SuppressFinalize(this);
}
}
关于源代码的问题:
这里我没有添加终结器,通常终止器将由GC调用,终结器将调用Dispose。 由于我没有终结器,我什么时候可以调用Dispose方法? 它是这个班的客户必须打电话吗?
所以我的例子中的类被称为NoGateway,客户端可以像这样使用和处理类:
using(NoGateway objNoGateway = new NoGateway())
{
// Do stuff here
}
当执行到达使用块的末尾时,会自动调用Dispose方法,还是客户端不得不手动调用dispose方法? 即
NoGateway objNoGateway = new NoGateway();
// Do stuff with object
objNoGateway.Dispose(); // finished with it
我在NoGateway类中使用NoGateway
类。 由于webclient实现了IDisposable接口,这是否意味着webclient间接使用非托管资源? 对此有没有严格而快速的规定? 我如何知道一个班级使用非托管资源?
建议的IDisposable模式在这里。 在编写使用IDisposable的类时,通常应使用两种模式:
在实现不使用非托管资源的密封类时,只需像使用常规接口一样实施Dispose方法:
public sealed class A : IDisposable
{
public void Dispose()
{
// get rid of managed resources, call Dispose on member variables...
}
}
当实现一个非密封类时,请这样做:
public class B : IDisposable
{
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
// get rid of managed resources
}
// get rid of unmanaged resources
}
// only if you use unmanaged resources directly in B
//~B()
//{
// Dispose(false);
//}
}
注意我没有在B
声明一个终结器; 你应该只实现一个终结器,如果你有实际的非托管资源来处置。 即使已调用SuppressFinalize
,CLR也会以不同于不可终止对象的方式处理可终结对象。
所以,除非必须声明一个终结器,否则你不应该声明一个终结器,但是如果他们直接使用非托管资源,你可以给你的类的继承者一个钩子来调用你的Dispose
并实现一个终结器:
public class C : B
{
private IntPtr m_Handle;
protected override void Dispose(bool disposing)
{
if (disposing)
{
// get rid of managed resources
}
ReleaseHandle(m_Handle);
base.Dispose(disposing);
}
~C() {
Dispose(false);
}
}
如果您没有直接使用非托管资源( SafeHandle
和朋友不计算,因为他们声明了自己的终结器),那么不要实现终结器,因为GC会以不同的方式处理可终结类,即使后来禁止终结器。 还要注意的是,即使B
没有终结器,它仍然会调用SuppressFinalize
来正确处理任何实现终结器的子类。
当一个类实现了IDisposable接口时,这意味着某个地方有一些非托管资源应该在完成使用该类时摆脱掉。 实际资源被封装在类中; 你不需要明确地删除它们。 只需调用Dispose()
或using(...) {}
封装类将确保任何非托管资源在必要时被删除。
实施IDisposable
的官方模式很难理解。 我相信这个更好:
public class BetterDisposableClass : IDisposable {
public void Dispose() {
CleanUpManagedResources();
CleanUpNativeResources();
GC.SuppressFinalize(this);
}
protected virtual void CleanUpManagedResources() {
// ...
}
protected virtual void CleanUpNativeResources() {
// ...
}
~BetterDisposableClass() {
CleanUpNativeResources();
}
}
一个更好的解决方案是有一个规则,你总是必须为你需要处理的任何非托管资源创建一个包装类:
public class NativeDisposable : IDisposable {
public void Dispose() {
CleanUpNativeResource();
GC.SuppressFinalize(this);
}
protected virtual void CleanUpNativeResource() {
// ...
}
~NativeDisposable() {
CleanUpNativeResource();
}
}
使用SafeHandle
及其衍生工具,这些类应该非常少见。
即使在存在继承的情况下,不直接处理非托管资源的可抛弃类的结果也非常强大: 它们不再需要关注非托管资源 。 他们将很容易实现并理解:
public class ManagedDisposable : IDisposable {
public virtual void Dispose() {
// dispose of managed resources
}
}
请注意,任何IDisposable实施应遵循以下模式(恕我直言)。 我基于来自几个优秀的.NET“众神”.NET框架设计指南的信息开发了这种模式(请注意,由于某些原因,MSDN不遵循这一点!)。 .NET框架设计指南由Krzysztof Cwalina(当时的CLR架构师)和Brad Abrams(我相信当时的CLR程序经理)和Bill Wagner([Effective C#]和[更高效的C#](只需要一个在Amazon.com上查找这些内容:
请注意,除非您的课程直接包含(不继承)非托管资源,否则您绝不应该实施终结器。 一旦你在一个类中实现了一个Finalizer,即使它从未被调用过,它也会保证为额外的集合而生活。 它会自动放置在Finalization Queue(在单个线程上运行)上。 另外,一个非常重要的注意事项...在Finalizer中执行的所有代码(如果您需要实现其中一个),必须是线程安全的并且是异常安全的! 不好的事情会发生,否则......(即未确定的行为,并在例外情况下,致命的不可恢复的应用程序崩溃)。
我放在一起的模式(并写了一段代码片段)如下:
#region IDisposable implementation
//TODO remember to make this class inherit from IDisposable -> $className$ : IDisposable
// Default initialization for a bool is 'false'
private bool IsDisposed { get; set; }
/// <summary>
/// Implementation of Dispose according to .NET Framework Design Guidelines.
/// </summary>
/// <remarks>Do not make this method virtual.
/// A derived class should not be able to override this method.
/// </remarks>
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.
// Always use SuppressFinalize() in case a subclass
// of this type implements a finalizer.
GC.SuppressFinalize( this );
}
/// <summary>
/// Overloaded Implementation of Dispose.
/// </summary>
/// <param name="isDisposing"></param>
/// <remarks>
/// <para><list type="bulleted">Dispose(bool isDisposing) executes in two distinct scenarios.
/// <item>If <paramref name="isDisposing"/> equals true, the method has been called directly
/// or indirectly by a user's code. Managed and unmanaged resources
/// can be disposed.</item>
/// <item>If <paramref name="isDisposing"/> 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.</item></list></para>
/// </remarks>
protected virtual void Dispose( bool isDisposing )
{
// TODO If you need thread safety, use a lock around these
// operations, as well as in your methods that use the resource.
try
{
if( !this.IsDisposed )
{
if( isDisposing )
{
// TODO Release all managed resources here
$end$
}
// TODO Release all unmanaged resources here
// TODO explicitly set root references to null to expressly tell the GarbageCollector
// that the resources have been disposed of and its ok to release the memory allocated for them.
}
}
finally
{
// explicitly call the base class Dispose implementation
base.Dispose( isDisposing );
this.IsDisposed = true;
}
}
//TODO Uncomment this code if this class will contain members which are UNmanaged
//
///// <summary>Finalizer for $className$</summary>
///// <remarks>This finalizer will run only if the Dispose method does not get called.
///// It gives your base class the opportunity to finalize.
///// DO NOT provide finalizers in types derived from this class.
///// All code executed within a Finalizer MUST be thread-safe!</remarks>
// ~$className$()
// {
// Dispose( false );
// }
#endregion IDisposable implementation
以下是在派生类中实现IDisposable的代码。 请注意,您不需要在派生类的定义中显式列出来自IDisposable的继承。
public DerivedClass : BaseClass, IDisposable (remove the IDisposable because it is inherited from BaseClass)
protected override void Dispose( bool isDisposing )
{
try
{
if ( !this.IsDisposed )
{
if ( isDisposing )
{
// Release all managed resources here
}
}
}
finally
{
// explicitly call the base class Dispose implementation
base.Dispose( isDisposing );
}
}
我已经在我的博客上发布了这个实现:如何正确地实现配置模式
链接地址: http://www.djcxy.com/p/9209.html