日历

2008 8.22 Fri
     12
3456789
10111213141516
17181920212223
24252627282930
31      
«» 2008 - 8 «»

文章搜索

日志文章

2008年04月29日 09:19:07

正确实现 IDisposable

正确实现 IDisposable



.NET中用于释放对象资源的接口是IDisposable,但是这个接口的实现还是比较有讲究的,此外还有FinalizeClose两个函数。

MSDN建议按照下面的模式实现IDisposable接口:.NET的对象中实际上有两个用于释放资源的函数:DisposeFinalizeFinalize的目的是用于释放非托管的资源,而Dispose是用于释放所有资源,包括托管的和非托管的。

1 public class Foo: IDisposable
2
{
3   public void
Dispose()
4
  {
5     Dispose(true
);
6     GC.SuppressFinalize(this
);
7
  }
8

9   protected virtual void Dispose(bool disposing)
10
  {
11     if (!
m_disposed)
12
    {
13         if
(disposing)
14
      {
15           // Release managed resources

16       }
17

18         // Release unmanaged resources

19
20         m_disposed = true
;
21
    }
22
  }
23

24   ~
Foo()
25
  {
26     Dispose(false
);
27
  }
28

29   private bool
m_disposed;
30
}
31

32




在这个模式中,void Dispose(bool disposing)函数通过一个disposing参数来区别当前是否是被Dispose()调用。如果是被Dispose()调用,那么需要同时释放托管和非托管的资源。如果是被~Foo()(也就是C#Finalize())调用了,那么只需要释放非托管的资源即可。



这是因为,Dispose()函数是被其它代码显式调用并要求释放资源的,而Finalize是被GC调用的。在GC调用的时候Foo所引用的其它托管对象可能还不需要被销毁,并且即使要销毁,也会由GC来调用。因此在Finalize中只需要释放非托管资源即可。另外一方面,由于在Dispose()中已经释放了托管和非托管的资源,因此在对象被GC回收时再次调用Finalize是没有必要的,所以在Dispose()中调用GC.SuppressFinalize(this)避免重复调用Finalize



然而,即使重复调用FinalizeDispose也是不存在问题的,因为有变量m_disposed的存在,资源只会被释放一次,多余的调用会被忽略过去。



因此,上面的模式保证了:



1、 Finalize只释放非托管资源;

2、 Dispose释放托管和非托管资源;

3、 重复调用FinalizeDispose是没有问题的;

4、 FinalizeDispose共享相同的资源释放策略,因此他们之间也是没有冲突的。



C#中,这个模式需要显式地实现,其中C#~Foo()函数代表了Finalize()。而在C++/CLI中,这个模式是自动实现的,C++的类析构函数则是不一样的。



按照C++语义,析构函数在超出作用域,或者delete的时候被调用。在Managed C++(即.NET 1.1中的托管C++)中,析构函数相当于CLR中的Finalize()方法,在垃圾收集的时候由GC调用,因此,调用的时机是不明确的。在.NET 2.0C++/CLI中,析构函数的语义被修改为等价与Dispose()方法,这就隐含了两件事情:



1、 所有的C++/CLI中的CLR类都实现了接口IDisposable,因此在C#中可以用using关键字来访问这个类的实例。

2、 析构函数不再等价于Finalize()了。



对于第一点,这是一件好事,我认为在语义上Dispose()更加接近于C++析构函数。对于第二点,Microsoft进行了一次扩展,做法是引入了“!”函数,如下所示:

1 public ref class Foo
2
{
3 public
:
4
    Foo();
5     ~Foo();     // destructor

6     !Foo();     // finalizer
7 };
8

“!”函数(我实在不知道应该怎么称呼它)取代原来Managed C++中的Finalize()GC调用。MSDN建议,为了减少代码的重复,可以写这样的代码:

1 ~Foo()
2
{
3   //释放托管的资源

4   this->!Foo();
5
}
6

7 !
Foo()
8
{
9   //释放非托管的资源

10 }
11

对于上面这个类,实际上C++/CLI生成对应的C#代码是这样的:





1 public class
Foo
2
{
3   private void !
Foo()
4
  {
5     // 释放非托管的资源

6   }
7

8   private void ~
Foo()
9
  {
10     // 释放托管的资源

11     !Foo();
12
  }
13

14   public
Foo()
15
  {
16
  }
17

18   public void
Dispose()
19
  {
20     Dispose(true
);
21     GC.SuppressFinalize(this
);
22
  }
23

24   protected virtual void Dispose(bool
disposing)
25
  {
26     if
(disposing)
27
    {
28         ~
Foo();
29
    }
30     else

31     {
32         try

33       {
34           !
Foo();
35
      }
36         finally

37       {
38           base
.Finalize();
39
      }
40
    }
41
  }
42

43   protected void
Finalize()
44
  {
45     Dispose(false
);
46
  }
47
}
48

由于~Foo()!Foo()不会被重复调用(至少MS这样认为),因此在这段代码中没有和前面m_disposed相同的变量,但是基本的结构是一样的。



并且,可以看到实际上并不是~Foo()!Foo()就是DisposeFinalize,而是C++/CLI编译器生成了两个DisposeFinalize函数,并在合适的时候调用它们。C++/CLI其实已经做了很多工作,但是唯一的一个问题就是依赖于用户在~Foo()中调用!Foo()



关于资源释放,最后一点需要提的是Close函数。在语义上它和Dispose很类似,按照MSDN的说法,提供这个函数是为了让用户感觉舒服一点,因为对于某些对象,例如文件,用户更加习惯调用Close()



然而,毕竟这两个函数做的是同一件事情,因此MSDN建议的代码就是:
Dispose函数以获得和Dispose相同的语义。这样似乎就圆满了,但是从另外一方面说,如果同时提供了DisposeClose,会给用户带来一些困惑。没有看到代码细节的前提下,很难知道这两个函数到底有什么区别。因此在.NET的代码设计规范中说,这两个函数实际上只能让用户用一个。因此建议的模式是:


1 public void Close()
2
{
3
  Dispose(();
4
}
5

6 这里直接调用不带参数的
1 public class Foo: IDisposable
2
{
3   public void
Close()
4
  {
5
    Dispose();
6
  }
7

8   void
IDisposable.Dispose()
9
  {
10     Dispose(true
);
11     GC.SuppressFinalize(this
);
12
  }
13

14   protected virtual void Dispose(bool
disposing)
15
  {
16     // 同前

17   }
18
}
19

这里使用了一个所谓的接口显式实现:void IDisposable.Dispose()。这个显式实现只能通过接口来访问,但是不能通过实现类来访问。因此:



1 Foo foo = new Foo();
2

3 foo.Dispose(); // 错误
4 (foo as IDisposable).Dispose(); // 正确
5



这样做到了兼顾两者。对于喜欢使用Close的人,可以直接用 foo.Close(),并且他看不到 Dispose()。对于喜欢Dispose的,他可以把类型转换为 IDisposable 来调用,或者使用using语句。两者皆大欢喜!


转自:http://www.cnblogs.com/xlshcn/archive/2007/01/16/idisposable.html

Tags: 资源释放  

类别: .net |  评论(2) |  浏览(1488) |  收藏
2楼 [匿名]gcusujck 2008年08月20日 12:35:05 Says:
%5Bb%5D%5Bsize=3%5D%E9%BE%99%E5%85%B4%E7%BD%91%E7%BB%9C%E6%98%AF%E5%9B%BD%E5%86%85%E4%B8
1楼 [匿名]7awzixrg 2008年08月14日 07:27:06 Says:
%5Burl=http://www.lxwltg.com/%5D%E5%88%9B%E9%B8%BF%E8%BD%AF%E4%BB%B6%EF%BC%8C%E7%8E%B0%E5%9C%A8%E5%8F%AF%E4%BB%A5%E5%90%914200%E5%AE%B6%E5%9B%BD%E5%86%85%E5%88%86%E7%B1%BB%E4%BF%A1%E6%81%AF%E6%B8%AF%E5%8F%91%E5%B8%83%E4%BE%9B%E6%B1%82%E4%BF%A1%E6%81%AF%EF%BC%8C600%E5%AE%B6%E7%9A%84%E6%90%9C%E7%B4%A2%E5%BC%95%E6%93%8E%E7%99%BB%E9%99%86%EF%BC%8C800%E5%AE%B6%E7%9A%84%E9%BB%84%E9%A1%B5%E7%99%BB%E9%99%86%EF%BC%8C%E5%B9%B6%E4%B8%94%E5%8C%85%E6%8B%AC36000%E4%B8%AA%E5%90%84%E4%B8%AA%E7%B1%BB%E5%88%AB%E7%9A%84%E5%8F%AF%E5%8F%91%E5%B8%83%E5%95%86%E5%8A%A1%E4%BF%A1%E6%81%AF%E7%9A%84%E7%BB%BC%E5%90%88%E7%BD%91%E7%AB%99%E4%BF%A1%E6%81%AF%E5%B9%BF%E6%92%AD%E5%8A%9F%E8%83%BD%EF%BC%8C%E6%88%90%E5%8A%9F%E7%8E%87%E6%97%A0%E5%8F%AF%E6%AF%94%E6%8B%9F%EF%BC%8C%E9%AA%8C%E8%AF%81%E7%A0%81%E5%87%A0%E4%B9%8E%E5%81%9A%E5%88%B0%E4%BA%86%E5%85%A8%E8%87%AA%E5%8A%A8%E8%AF%86%E5%88%AB%EF%BC%8C%E8%87%AA%E5%8A%A8%E5%A1%AB%E5%86%99%EF%BC%81%E5%B9%B6%E4%B8%94%E5%A2%9E%E5%8A%A0%E4%BA%86%E6%AF%8F%E5%A4%A9%E6%88%96%E6%AF%8F%E5%91%A8%E6%88%96%E6%AF%8F%E6%9C%88%E5%85%A8%E8%87%AA%E5%8A%A8%E6%89%A7%E8%A1%8C%E8%AE%A1%E5%88%92%E4%BB%BB%E5%8A%A1%E7%9A%84%E5%BC%BA%E5%A4%A7%E5%8A%9F%E8%83%BD%EF%BC%81%E6%97%A0%E9%9C
0楼 [匿名]0u6ej0tn 2008年08月13日 19:59:37 Says:
%5Burl=http://www.lxwltg.com/%5D%E5%88%9B%E9%B8%BF%E8%BD%AF%E4%BB%B6%EF%BC%8C%E7%8E%B0%E5%9C%A8%E5%8F%AF%E4%BB%A5%E5%90%914200%E5%AE%B6%E5%9B%BD%E5%86%85%E5%88%86%E7%B1%BB%E4%BF%A1%E6%81%AF%E6%B8%AF%E5%8F%91%E5%B8%83%E4%BE%9B%E6%B1%82%E4%BF%A1%E6%81%AF%EF%BC%8C600%E5%AE%B6%E7%9A%84%E6%90%9C%E7%B4%A2%E5%BC%95%E6%93%8E%E7%99%BB%E9%99%86%EF%BC%8C800%E5%AE%B6%E7%9A%84%E9%BB%84%E9%A1%B5%E7%99%BB%E9%99%86%EF%BC%8C%E5%B9%B6%E4%B8%94%E5%8C%85%E6%8B%AC36000%E4%B8%AA%E5%90%84%E4%B8%AA%E7%B1%BB%E5%88%AB%E7%9A%84%E5%8F%AF%E5%8F%91%E5%B8%83%E5%95%86%E5%8A%A1%E4%BF%A1%E6%81%AF%E7%9A%84%E7%BB%BC%E5%90%88%E7%BD%91%E7%AB%99%E4%BF%A1%E6%81%AF%E5%B9%BF%E6%92%AD%E5%8A%9F%E8%83%BD%EF%BC%8C%E6%88%90%E5%8A%9F%E7%8E%87%E6%97%A0%E5%8F%AF%E6%AF%94%E6%8B%9F%EF%BC%8C%E9%AA%8C%E8%AF%81%E7%A0%81%E5%87%A0%E4%B9%8E%E5%81%9A%E5%88%B0%E4%BA%86%E5%85%A8%E8%87%AA%E5%8A%A8%E8%AF%86%E5%88%AB%EF%BC%8C%E8%87%AA%E5%8A%A8%E5%A1%AB%E5%86%99%EF%BC%81%E5%B9%B6%E4%B8%94%E5%A2%9E%E5%8A%A0%E4%BA%86%E6%AF%8F%E5%A4%A9%E6%88%96%E6%AF%8F%E5%91%A8%E6%88%96%E6%AF%8F%E6%9C%88%E5%85%A8%E8%87%AA%E5%8A%A8%E6%89%A7%E8%A1%8C%E8%AE%A1%E5%88%92%E4%BB%BB%E5%8A%A1%E7%9A%84%E5%BC%BA%E5%A4%A7%E5%8A%9F%E8%83%BD%EF%BC%81%E6%97%A0%E9%9C
发表评论