我的 IDisposable 是否在我的班级中正确实现或者需要一些更改

发布于 2024-12-08 13:49:52 字数 2683 浏览 1 评论 0 原文

我在 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 接口,或者我需要对上面的代码进行一些更改。

I have got below code in C# 2.0 and I am trying to implement IDisposable in my class.

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

    }
}   

Please suggest whether I have implemnted IDisposable interface correclty or I need to do some code changes above.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

困倦 2024-12-15 13:49:52

这不是正常的 IDisposable 模式,通常您会做类似的事情:

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

That's not the normal IDisposable pattern, typically you'd do something more like:

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
    }
}
喜你已久 2024-12-15 13:49:52

您应该添加一个调用 Cleanup 的终结器。如果有人没有按照应有的方式调用 Dispose,则非托管资源最终将被垃圾收集器清理。

这就是为什么您首先调用 GC.SuppressFinalize(this); 的原因,以确保在正确处置对象的情况下 GC 不会调用您的终结器。

编辑

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

public ~PageMetaDataValues(){
  Cleanup();
}

但是您的代码中还有一些其他奇怪的情况,例如锁定 this 等。另外,自从您首先添加锁定以来,您是否期望从多个不同的线程中处理同一个对象?

You should add a finializer that calls Cleanup to. In case someone don't call Dispose as they should, the unmanaged resources will then eventually be cleaned up by the garbage collector.

That's why you are calling GC.SuppressFinalize(this); in the first place, to make sure that the GC does not call your finalizer if the object was disposed properly.

EDIT

That means that you at least need to add:

public ~PageMetaDataValues(){
  Cleanup();
}

There are however some other strange cases in your code like locking on this and so on. Also, are you expecting to dispose the same object from several different threads since you added the locking in the first place?

森林散布 2024-12-15 13:49:52

只需更改

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

Just change

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

    }

to

    public void IDisposable.Dispose()
    {   
            lock (this)
            {
                if (m_Disposed == false)
                {
                    Cleanup();
                    m_Disposed = true;
                    GC.SuppressFinalize(this);
                }
            }
    }
鸩远一方 2024-12-15 13:49:52

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

现在我们将看看另一个问题:您正在尝试实现线程安全的 IDisposable 模式。正确地做到这一点是相当复杂的。让我们看看简单的解决方案。你的(经过 Controlpower 的更正)是可以的:-)显然你应该检查每个公共方法/属性开头的 Dispose 属性,如果它是 true,你应该抛出一个 ObjectDisposeException< /代码>。更好(更快)的实现是 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
    }
}

As written by others (the ControlPower solution is correct), the Dispose method is a method, not a property :-).

Now we will look at another problem: you are trying to implement a thread safe IDisposable pattern. To do it correctly is quite complex. Let's see the naive solution. Yours (with the corrections of Controlpower) is ok :-) Clearly you should check the Disposed property at the beginning of each public method/property and if it's true, you should throw an ObjectDisposedException. A better (faster) implementation is the one given by http://blogs.msdn.com/b/blambert/archive/2009/07/24/a-simple-and-totally-thread-safe-implementation-of-idisposable.aspx

he uses Interlocked.CompareExchange instead of lock. It's faster.

It still suffers from one problem: if Thread A executes method A1 and at the same time Thread B executes a Dispose of the object, Thread A could be mid-method when the object is disposed. I'll give you a third solution. It used ReaderWriterLockSlim. Every public method/property takes a reader lock when it executes. Dispose takes a write lock when it executes. Many "normal" methods can be executed at the same time. Dispose needs to be executed when no one else is executing. Private and protected methods/properties (that aren't part of an interface) don't need to check for the disposed state because they are called from public methods/properties.

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
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文