关于c#:从派生类自动调用base.Dispose()

关于c#:从派生类自动调用base.Dispose()

Calling base.Dispose() automatically from derived classes

编辑-新问题

好的,让我们更笼统地说一下问题。

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

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

背景

我们正在用C#3.0开发具有相对较大的类层次结构的系统。这些类中的某些类在层次结构中的任何地方都具有需要
弃用的那些实现IDisposable接口的对象。

问题

现在,为了方便代码的维护和重构,我想为实现IDisposable的类找到一种方法,
"自动"调用base.Dispose(bDisposed)(如果有祖先也实现IDisposable)。这样,如果层次结构中较高的某个类开始实施
或停止实施将自动处理的IDisposable。

问题有两个方面。

  • 首先,查找是否有祖先实现IDisposable。
  • 其次,有条件地调用base.Dispose(bDisposed)。

第一部分,找到有关实现IDisposable的祖先的信息,我已经能够解决。

第二部分是棘手的部分。尽管我所有
的努力,我无法从派生类调用base.Dispose(bDisposed)。我所有的尝试都失败了。他们要么造成
编译错误或称为错误的Dispose()方法,这是派生程度最高的方法,从而永远循环。

主要的问题是,如果没有这样的东西,您实际上不能直接在代码中直接引用base.Dispose()。
祖先实现它(提醒您,可能尚没有祖先实现IDisposable,但我希望派生代码在这种情况下以及当
将来会发生某件事)。这使我们有了反思机制,但是我没有找到适当的方法来做到这一点。我们的代码充满了
先进的反射技术,我想我在那里什么都没错过。

我的解决方案

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
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());
            }
        }
    }
}

所以,我问有一种方法可以自动/有条件地调用base.Dispose()吗?

更多背景

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


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

终结器,在C#中拼写为?Class:不要。很少有一个类需要一个,而意外保留大型对象图非常容易,因为终结器在释放内存之前至少需要两个集合。在不再引用该对象之后的第一个集合上,将其放到要运行的终结器队列中。它们在单独的专用线程上运行,该线程仅运行终结器(如果阻塞了终结器,则不再运行终结器,并且内存使用量会爆炸)。终结器运行后,收集适当世代的下一个集合将释放该对象以及该对象所引用的其他任何未引用的对象。不幸的是,由于它可以在第一个收藏中保存下来,因此将被放到较旧的收藏中,而收藏的频率则较低。因此,您应该及早处置。

通常,您应该实现一个小的资源包装器类,该类仅管理资源生存期,并在该类上实现终结器以及IDisposable。然后,该类的用户应在处理它时对此调用Dispose。不应存在??指向用户的反向链接。这样,只有真正需要完成的事情才最终出现在完成队列中。

如果在层次结构中的任何位置都需要它们,则实现IDisposable的基类应实现finalizer并调用Dispose(bool),并传递false作为参数。

Windows Mobile开发人员警告(VS2005和2008,.NET Compact Framework 2.0和3.5):许多掉在设计器表面的非控件,例如菜单栏,计时器,HardwareButtons从System.ComponentModel.Component派生,后者实现了终结器。对于桌面项目,Visual Studio将这些组件添加到名为components的System.ComponentModel.Container中,当Dispose释放该表单时,它会生成要处置的代码-依次处置所有已添加的组件。对于移动项目,将生成处理components的代码,但是将组件放到表面上不会生成将其添加到components的代码。调用InitializeComponent之后,必须在构造函数中自行执行此操作。


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

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


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
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
            }
        }
    }

}

没有做到这一点的"公认"方法。 您真的想使清理逻辑(无论它运行在Dispose还是终结器中)尽可能地简单,以免失败。 在处理(尤其是终结器)内部使用反射通常是个坏主意。

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

请参阅本文以获取有关Dispose模式的更多信息。


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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
// 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);
    }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
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
    }
}

这样,您有责任仅对IDisposable的第一类实施权利。


如果您想使用[basetype] .Invoke(" Dispose" ...),则可以实现函数调用而无需调试器抱怨。然后,当基本类型实际实现IDisposable接口时,它将执行适当的调用。


如果您想使用[basetype] .Invoke(" Dispose" ...),则可以实现函数调用而无需调试器抱怨。然后,当基本类型实际实现IDisposable接口时,它将执行适当的调用。


推荐阅读