我的IDisposable是否在我的课程中正确实现或需要进行一些更改

时间:2011-10-06 07:45:12

标签: c# idisposable

我在C#2.0中有以下代码,我正在尝试在我的课程中实现IDisposable。

using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
using System.Xml;
using System.Xml.XPath;
using System.Xml.Xsl;
using Tridion.ContentManager;
using Tridion.ContentManager.Templating.Assembly;
using Tridion.ContentManager.Templating;
using Tridion.ContentManager.ContentManagement;
using Tridion.ContentManager.ContentManagement.Fields;
using Emirates.Tridion.BuildingBlocks.Base;
using th = my.Tridion.BuildingBlocks.Base.TemplateHelper;
using ut = my.Tridion.BuildingBlocks.Base.Utilities;
using tc = Tridion.ContentManager.CommunicationManagement;
using System.IO;
using System.Globalization;
using System.Threading;

namespace my.BuildingBlocks.Utilities
{

    [TcmTemplateTitle("Page Metadata Values")]
    public class PageMetaDataValues : TemplateBase, IDisposable
    {

        private bool m_Disposed = false;

        protected bool Disposed
        {
            get
            {
                lock (this)
                {
                    return (m_Disposed);
                }
            }
        }


        public override void Transform(Engine engine, Package package)
        {
            Initialize(engine, package);
            m_Logger.Info("Start of Page Metadata Values");
            tc.Publication pubObject= m_Engine.GetSession().GetObject(m_Publication.Id) as tc.Publication;          
            if (pubObject != null)
            {
                Thread.CurrentThread.CurrentCulture = new CultureInfo(ut.RenderPageLocale(pubObject));
            }
            package.PushItem("PageMetaDataValues", package.CreateStringItem(ContentType.Xml, RenderCurrentPageXML()));
            m_Logger.Info("End of Page Metadata Values");
        }

        private string RenderCurrentPageXML()
        {
            m_Logger.Info("Rendering the Page Metadata Values");

            XmlDocument pageDoc = new XmlDocument();
            pageDoc.LoadXml(GetCurrentPageXML(m_Page.Id));
            return pageDoc.InnerXml;
        }      

        void IDisposable.Dispose
        {   
            get
            {
                lock (this)
                {
                    if (m_Disposed == false)
                    {
                        Cleanup();
                        m_Disposed = true;
                        GC.SuppressFinalize(this);
                    }
                }
            }

        }
        protected virtual void Cleanup()
        {
            /* do cleanup of unmanaged resources here */
        }
        #endregion

    }
}   

请建议我是否已实现IDisposable接口correclty,或者我需要在上面进行一些代码更改。

4 个答案:

答案 0 :(得分:1)

这不是正常的IDisposable pattern,通常你会做更多的事情:

public class DisposableClass : IDisposable
{
    ~DisposableClass()
    {
        Dispose(false);
    }

    private bool disposed = false;
    protected bool Disposed
    {
        get
        {
            return (disposed);
        }
    }

    public override void Transform(Engine engine, Package package)
    {
        if( Disposed )
        {
             throw new ObjectDisposedException();
        }

        Initialize(engine, package);
        m_Logger.Info("Start of Page Metadata Values");
        tc.Publication pubObject= m_Engine.GetSession().GetObject(m_Publication.Id) as tc.Publication;          
        if (pubObject != null)
        {
            Thread.CurrentThread.CurrentCulture = new CultureInfo(ut.RenderPageLocale(pubObject));
        }
        package.PushItem("PageMetaDataValues", package.CreateStringItem(ContentType.Xml, RenderCurrentPageXML()));
        m_Logger.Info("End of Page Metadata Values");
    }

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

    protected virtual void Dispose(bool disposing)
    {
        if (disposing)
        {
            disposed = true;
            // Clean up all managed resources
        }

        // Clean up all native resources
    }
}

答案 1 :(得分:0)

您应该添加一个调用Cleanup的终结器。如果有人不按预期调用Dispose,那么非托管资源最终将被垃圾收集器清理。

这就是您首先调用GC.SuppressFinalize(this);的原因,以确保如果对象处理得当,GC不会调用终结器。

修改

这意味着您至少需要添加:

public ~PageMetaDataValues(){
  Cleanup();
}

然而,您的代码中还存在其他一些奇怪的情况,例如锁定this等等。此外,您是否期望从几个不同的线程中处置相同的对象,因为您首先添加了锁定?

答案 2 :(得分:0)

只需更改

    void IDisposable.Dispose
    {   
        get
        {
            lock (this)
            {
                if (m_Disposed == false)
                {
                    Cleanup();
                    m_Disposed = true;
                    GC.SuppressFinalize(this);
                }
            }
        }

    }

    public void IDisposable.Dispose()
    {   
            lock (this)
            {
                if (m_Disposed == false)
                {
                    Cleanup();
                    m_Disposed = true;
                    GC.SuppressFinalize(this);
                }
            }
    }

答案 3 :(得分:0)

正如其他人所写的(ControlPower解决方案是正确的),Dispose方法是一种方法,而不是属性: - )。

现在我们将看另一个问题:您正在尝试实现线程安全的IDisposable模式。要做到这一点非常复杂。让我们看看天真的解决方案。你的(使用Controlpower的更正)是可以的:-)显然你应该检查每个公共方法/属性开头的Disposed属性,如果是,你应该抛出ObjectDisposedException。更好(更快)的实现是http://blogs.msdn.com/b/blambert/archive/2009/07/24/a-simple-and-totally-thread-safe-implementation-of-idisposable.aspx

给出的实现

他使用Interlocked.CompareExchange代替lock。它更快。

它仍然存在一个问题:如果线程A执行方法A1并且同时线程B执行对象的Dispose,则在处理对象时,线程A可以是中间方法。我会给你第三个解决方案。它使用了ReaderWriterLockSlim。每个公共方法/属性在执行时都会锁定读取器。 Dispose在执行时会执行写锁定。许多“正常”方法可以同时执行。当没有其他人正在执行时,需要执行Dispose。私有和受保护的方法/属性(不是接口的一部分)不需要检查处置状态,因为它们是从公共方法/属性调用的。

class MyClass : IDisposable
{
    protected ReaderWriterLockSlim disposeLock = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion);
    protected bool isDisposed = false; 

    ~MyClass()
    {
        this.Dispose(false);
    }

    public int ExamplePublicProperty
    {
        get
        {
            disposeLock.EnterReadLock();

            try
            {
                if (isDisposed)
                {
                    throw new ObjectDisposedException(GetType().FullName);
                }

                // The property

                return 0; // What you want to return
            }
            finally
            {
                disposeLock.ExitReadLock();
            }
        }

        set
        {
            disposeLock.EnterReadLock();

            try
            {
                if (isDisposed)
                {
                    throw new ObjectDisposedException(GetType().FullName);
                }

                // The property
            }
            finally
            {
                disposeLock.ExitReadLock();
            }
        }
    }

    // The same for private methods
    protected int ExampleProtectedProperty
    {
        // Here we don't need to check for isDisposed
        get;
        set;
    }

    public void ExamplePublicMethod()
    {
        disposeLock.EnterReadLock();

        try
        {
            if (isDisposed)
            {
                throw new ObjectDisposedException(GetType().FullName);
            }

            // The method
        }
        finally
        {
            disposeLock.ExitReadLock();
        }
    }

    // The same for private methods
    protected void ExampleProtectedMethod()
    {
        // Here we don't need to check for isDisposed
    }

    #region IDisposable Members

    public void Dispose()
    {
        disposeLock.EnterWriteLock();

        try
        {
            if (isDisposed)
            {
                return;
            }

            Dispose(true);
            GC.SuppressFinalize(this);

            isDisposed = true;
        }
        finally
        {
            disposeLock.ExitWriteLock();
        }
    }

    #endregion

    protected virtual void Dispose(bool disposing)
    {
        // do the freeing
    }
}