C# 从派生类自动调用 base.Dispose()

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/78141/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-03 11:16:02  来源:igfitidea点击:

Calling base.Dispose() automatically from derived classes

提问by Philibert Perusse

Edit - New Question

编辑 - 新问题

Ok lets rephrase the question more generically.

好的,让我们更笼统地重新表述这个问题。

Using reflection, is there a way to dynamically call at runtime a base class method that you may be overriding. You cannot use the 'base' keyword at compile time because you cannot be sure it exists. At runtime I want to list my ancestors methods and call the ancestor methods.

使用反射,有没有办法在运行时动态调用您可能会覆盖的基类方法。您不能在编译时使用 'base' 关键字,因为您无法确定它是否存在。在运行时,我想列出我的祖先方法并调用祖先方法。

I tried using GetMethods() and such but all they return are "pointers" to the most derived implementation of the method. Not an implementation on a base class.

我尝试使用 GetMethods() 等,但它们返回的只是指向该方法的最派生实现的“指针”。不是基类上的实现。

Background

背景

We are developing a system in C# 3.0 with a relatively big class hierarchy. Some of these classes, anywhere in the hierarchy, have resources that need to be disposed of, those implement the IDisposableinterface.

我们正在用 C# 3.0 开发一个具有相对较大类层次结构的系统。其中一些类,在层次结构中的任何地方,都有需要处理的资源,这些资源实现了IDisposable接口。

The Problem

问题

Now, to facilitate maintenance and refactoring of the code I would like to find a way, for classes implementing IDisposable, to "automatically" call base.Dispose(bDisposing)if any ancestors also implements IDisposable. This way, if some class higher up in the hierarchy starts implementing or stops implementing IDisposable that will be taken care of automatically.

现在,为了便于代码的维护和重构,我想找到一种方法,对于实现 IDisposable 的类,如果任何祖先也实现了 IDisposable ,则“自动”调用base.Dispose(bDisposing)。这样,如果层次结构中更高的某个类开始实现或停止实现 IDisposable,它将被自动处理。

The issue is two folds.

这个问题有两个方面。

  • First, finding if any ancestors implements IDisposable.
  • Second, calling base.Dispose(bDisposing) conditionally.
  • 首先,查找是否有任何祖先实现了 IDisposable。
  • 其次,有条件地调用 base.Dispose(bDisposing)。

The first part, finding about ancestors implementing IDisposable, I have been able to deal with.

第一部分,找到关于实现IDisposable的祖先,我已经能够处理了。

The second part is the tricky one. Despite all my efforts, I haven't been able to call base.Dispose(bDisposing) from a derived class. All my attempts failed. They either caused compilation errors or called the wrong Dispose() method, that is the most derived one, thus looping forever.

第二部分是棘手的部分。尽管我付出了很多努力,但我还是无法从派生类调用 base.Dispose(bDisposing)。我所有的尝试都失败了。它们要么导致编译错误,要么调用了错误的 Dispose() 方法,这是派生最多的方法,从而永远循环。

The main issue is that you cannot actually refer to base.Dispose()directly in your code if there is no such thing as an ancestor implementing it (be reminded that there might have no ancestors yet implementing IDisposable, but I want the derived code to be ready when and if such a thing happens in the future). That leave us with the Reflectionmechanisms, but I did not find a proper way of doing it. Our code is quite filled with advanced reflection techniques and I think I did not miss anything obvious there.

主要问题是,如果没有实现它的祖先之类的东西,您实际上不能在代码中直接引用 base.Dispose()(请注意,可能还没有实现 IDisposable 的祖先,但我希望派生代码如果将来发生此类事情,请做好准备)。这给我们留下了反射机制,但我没有找到正确的方法。我们的代码充满了高级反射技术,我想我没有遗漏任何明显的东西。

My Solution

我的解决方案

My best shot yet was to have some conditional code using in commented code. Changing the IDisposable hierarchy would either break the build (if no IDisposable ancestor exists) or throw an exception (if there are IDisposable ancestors but base.Dispose is not called).

我最好的尝试是在注释代码中使用一些条件代码。更改 IDisposable 层次结构将破坏构建(如果不存在 IDisposable 祖先)或引发异常(如果存在 IDisposable 祖先但未调用 base.Dispose)。

Here is some code I am posting to show you what my Dispose(bDisposing) method looks like. I am putting this code at the end of all the Dispose() methods throughout the hierarchy. Any new classes are created from templates that also includes this code.

这是我发布的一些代码,向您展示我的 Dispose(bDisposing) 方法的外观。我将此代码放在整个层次结构中所有 Dispose() 方法的末尾。任何新类都是从也包含此代码的模板创建的。

public class MyOtherClassBase
{
    // ...
}


public class MyDerivedClass : MyOtherClassBase, ICalibrable
{

    private bool m_bDisposed = false;

    ~MyDerivedClass()
    {
        Dispose(false);
    }

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

    protected virtual void Dispose(bool bDisposing)
    {
        if (!m_bDisposed) {
            if (bDisposing) {
                // Dispose managed resources
            }
            // Dispose unmanaged resources
        }
        m_bDisposed = true;

        Type baseType = typeof(MyDerivedClass).BaseType;
        if (baseType != null) {
            if (baseType.GetInterface("IDisposable") != null) {
                // If you have no ancestors implementing base.Dispose(...), comment
                // the following line AND uncomment the throw. 
                //
                // This way, if any of your ancestors decide one day to implement 
                // IDisposable you will know about it right away and proceed to 
                // uncomment the base.Dispose(...) in addition to commenting the throw.
                //base.Dispose(bDisposing);
                throw new ApplicationException("Ancestor base.Dispose(...) not called - " 
                                               + baseType.ToString());
            }
        }
    }
}

So, I am asking is there a way to call base.Dispose() automatically/conditionally instead?

所以,我问有没有办法自动/有条件地调用 base.Dispose() ?

More Background

更多背景

There is another mechanism in the application where all objects are registered with a main class. The class checks if they implement IDisposable. If so, they are disposed of properly by the application. This avoids having the code using the classes to deal with calling Dispose() all around by themselves. Thus, adding IDisposable to a class that has no ancestor history of IDisposable still works perfectly.

应用程序中还有另一种机制,其中所有对象都注册到一个主类。该类检查它们是否实现了 IDisposable。如果是这样,它们会被应用程序正确处理。这避免了使用类的代码自己处理调用 Dispose()。因此,将 IDisposable 添加到没有 IDisposable 祖先历史的类仍然可以正常工作。

回答by Bryant

Personally, I think you might be better off handling this with something like FxCop. You should be able to write a rule that check so see if when an object is created that implements IDisposable that you use a using statement.

就个人而言,我认为您最好使用 FxCop 之类的工具来处理此问题。您应该能够编写一个规则来检查是否在创建实现 IDisposable 的对象时使用 using 语句。

It seems a little dirty (to me) to automatically dispose an object.

自动处理一个对象似乎有点脏(对我来说)。

回答by Bryant

If you wanted to use [basetype].Invoke("Dispose"...) then you could implement the function call without the debugger complaining. Then later when the base type actually implements the IDisposable interface it will execute the proper call.

如果你想使用 [basetype].Invoke("Dispose"...) 那么你可以在没有调试器抱怨的情况下实现函数调用。然后,当基类型实际实现 IDisposable 接口时,它将执行正确的调用。

回答by Adam Driscoll

If you wanted to use [basetype].Invoke("Dispose"...) then you could implement the function call without the debugger complaining. Then later when the base type actually implements the IDisposable interface it will execute the proper call.

如果你想使用 [basetype].Invoke("Dispose"...) 那么你可以在没有调试器抱怨的情况下实现函数调用。然后,当基类型实际实现 IDisposable 接口时,它将执行正确的调用。

回答by Mike Dimmick

The standard pattern is for your base class to implement IDisposable and the non-virtual Dispose() method, and to implement a virtual Dispose(bool) method, which those classes which hold disposable resources must override. They should always call their base Dispose(bool) method, which will chain up to the top class in the hierarchy eventually. Only those classes which override it will be called, so the chain is usually quite short.

标准模式是让您的基类实现 IDisposable 和非虚拟 Dispose() 方法,并实现虚拟 Dispose(bool) 方法,那些持有一次性资源的类必须覆盖该方法。他们应该始终调用他们的基本 Dispose(bool) 方法,该方法最终将链接到层次结构中的顶级类。只有那些覆盖它的类才会被调用,所以链通常很短。

Finalizers, spelled ~Class in C#: Don't. Very few classes will need one, and it's very easy to accidentally keep large object graphs around, because the finalizers require at least two collections before the memory is released. On the first collection after the object is no longer referenced, it's put on a queue of finalizers to be run. These are run on a separate, dedicated threadwhich only runs finalizers (if it gets blocked, no more finalizers run and your memory usage explodes). Once the finalizer has run, the next collection that collects the appropriate generation will free the object and anything else it was referencing that isn't otherwise referenced. Unfortunately, because it survives the first collection, it will be placed into the older generation which is collected less frequently. For this reason, you should Dispose early and often.

终结器,在 C# 中拼写为 ~Class:不要。很少有类需要一个,并且很容易意外地保留大型对象图,因为在释放内存之前终结器至少需要两个集合。在对象不再被引用后的第一个集合上,它被放在要运行的终结器队列中。这些在单独的专用线程上运行它只运行终结器(如果它被阻塞,则不再有终结器运行并且你的内存使用量会爆炸)。一旦终结器运行,下一个收集适当代的集合将释放对象以及它引用的任何其他未引用的对象。不幸的是,因为它在第一个集合中幸存下来,所以它会被放入收集频率较低的老年代。因此,您应该尽早并经常处理。

Generally, you should implement a small resource wrapper class that onlymanages the resource lifetime and implement a finalizer on that class, plus IDisposable. The user of the class should then call Dispose on this when it is disposed. There shouldn't be a back-link to the user. That way, only the thing that actually needs finalization ends up on the finalization queue.

通常,您应该实现一个管理资源生命周期的小型资源包装类,并在该类上实现终结器,以及 IDisposable。类的用户应该在它被释放时调用它的 Dispose。不应该有用户的反向链接。这样,只有真正需要终结的东西才会出现在终结队列中。

If you are going to need them anywhere in the hierarchy, the base class that implements IDisposable should implement the finalizer and call Dispose(bool), passing false as the parameter.

如果在层次结构中的任何地方都需要它们,实现 IDisposable 的基类应该实现终结器并调用 Dispose(bool),将 false 作为参数传递。

WARNING for Windows Mobile developers (VS2005 and 2008, .NET Compact Framework 2.0 and 3.5): many non-controls that you drop onto your designer surface, e.g. menu bars, timers, HardwareButtons, derive from System.ComponentModel.Component, which implements a finalizer. For desktop projects, Visual Studio adds the components to a System.ComponentModel.Container named components, which it generates code to Dispose when the form is Disposed - it in turn Disposes all the components that have been added. For the mobile projects, the code to Dispose componentsis generated, but dropping a component onto the surface does not generate the code to add it to components. You have to do this yourself in your constructor after calling InitializeComponent.

Windows Mobile 开发人员警告(VS2005 和 2008,.NET Compact Framework 2.0 和 3.5):您放置在设计器界面上的许多非控件,例如菜单栏、计时器、硬件按钮,派生自 System.ComponentModel.Component,它实现了终结者。对于桌面项目,Visual Studio 将组件添加到名为 的 System.ComponentModel.Container 中components,当表单被 Disposed 时,它会生成代码以进行 Dispose - 它反过来 Dispose 所有已添加的组件。对于移动项目,components会生成Dispose 的代码,但将组件放到表面上不会生成将其添加到components. 调用 InitializeComponent 后,您必须在构造函数中自己执行此操作。

回答by Sunny Milenov

public class MyVeryBaseClass {
    protected void RealDispose(bool isDisposing) {
        IDisposable tryme = this as IDisposable;
        if (tryme != null) { // we implement IDisposable
            this.Dispose();
            base.RealDispose(isDisposing);
        }
    }
}
public class FirstChild : MyVeryBaseClasee {
    //non-disposable
}
public class SecondChild : FirstChild, IDisposable {
    ~SecondChild() {
        Dispose(false);
    }
    public void Dispose() {
        Dispose(true);
        GC.SuppressFinalize(this);
        base.RealDispose(true);
    }
    protected virtual void Dispose(bool bDisposing) {
        if (!m_bDisposed) {
            if (bDisposing) {
            }// Dispose managed resources
        } // Dispose unmanaged resources
    }
}

That way, you are responsible to implement right only the first class which is IDisposable.

这样,您只负责实现第一个 IDisposable 类。

回答by Steve Cooper

Try this. It's a one-line addition to the Dispose() method, and calls the ancestor's dispose, if it exists. (Note that Dispose(bool)is not a member of IDisposable)

尝试这个。它是对 Dispose() 方法的一行添加,并调用祖先的 dispose(如果存在)。(注意Dispose(bool)不是 的成员IDisposable

// Disposal Helper Functions
public static class Disposing
{
    // Executes IDisposable.Dispose() if it exists.
    public static void DisposeSuperclass(object o)
    {
        Type baseType = o.GetType().BaseType;
        bool superclassIsDisposable = typeof(IDisposable).IsAssignableFrom(baseType);
        if (superclassIsDisposable)
        {
            System.Reflection.MethodInfo baseDispose = baseType.GetMethod("Dispose", new Type[] { });
            baseDispose.Invoke(o, null);
        }
    }
}

class classA: IDisposable
{
    public void Dispose()
    {
        Console.WriteLine("Disposing A");
    }
}

class classB : classA, IDisposable
{
}

class classC : classB, IDisposable
{
    public void Dispose()
    {
        Console.WriteLine("Disposing C");
        Disposing.DisposeSuperclass(this);
    }
}

回答by Scott Dorman

There is not an "accepted" way of doing this. You really want to make your clean up logic (whether it runs inside of a Dispose or a finalizer) as simple as possible so it won't fail. Using reflection inside of a dispose (and especially a finalizer) is generally a bad idea.

没有一种“被接受”的方式来做到这一点。你真的想让你的清理逻辑(无论是在 Dispose 还是终结器中运行)尽可能简单,这样它就不会失败。在处置(尤其是终结器)中使用反射通常是一个坏主意。

As far as implementing finalizers, in general you don't need to. Finalizers add a cost to your object and are hard to write correctly as most of the assumptions you can normally make about the state of the object and the runtime are not valid.

至于实现终结器,一般来说你不需要。终结器给您的对象增加了成本并且很难正确编写,因为您通常可以对对象的状态和运行时做出的大多数假设都是无效的。

See this articlefor more information on the Dispose pattern.

有关Dispose 模式的更多信息,请参阅本文

回答by SUmeet Khandelwal

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace TestDisposeInheritance
{
    class Program
    {
        static void Main(string[] args)
        {
            classC c = new classC();
            c.Dispose();
        }
    }

    class classA: IDisposable 
    { 
        private bool m_bDisposed;
        protected virtual void Dispose(bool bDisposing)
        {
            if (!m_bDisposed)
            {
                if (bDisposing)
                {
                    // Dispose managed resources
                    Console.WriteLine("Dispose A"); 
                }
                // Dispose unmanaged resources 
            }
        }
        public void Dispose() 
        {
            Dispose(true);
            GC.SuppressFinalize(this);
            Console.WriteLine("Disposing A"); 
        } 
    } 

    class classB : classA, IDisposable 
    {
        private bool m_bDisposed;
        public void Dispose()
        {
            Dispose(true);
            base.Dispose();
            GC.SuppressFinalize(this);
            Console.WriteLine("Disposing B");
        }

        protected override void Dispose(bool bDisposing)
        {
            if (!m_bDisposed)
            {
                if (bDisposing)
                {
                    // Dispose managed resources
                    Console.WriteLine("Dispose B");
                }
                // Dispose unmanaged resources 
            }
        }
    } 

    class classC : classB, IDisposable 
    {
        private bool m_bDisposed;
        public void Dispose() 
        {
            Dispose(true);
            base.Dispose();
            GC.SuppressFinalize(this);
            Console.WriteLine("Disposing C");             
        }
        protected override void Dispose(bool bDisposing)
        {
            if (!m_bDisposed)
            {
                if (bDisposing)
                {
                    // Dispose managed resources
                    Console.WriteLine("Dispose C");             
                }
                // Dispose unmanaged resources 
            }
        }
    } 

}