now*_*ed. 4 .net c# garbage-collection .net-4.5
今天,我想用文件执行操作,所以我想出了这段代码
class Test1
{
Test1()
{
using (var fileStream = new FileStream("c:\\test.txt", FileMode.Open))
{
//just use this filestream in a using Statement and release it after use.
}
}
}
Run Code Online (Sandbox Code Playgroud)
但在代码审查中,我被要求实现IDisposable接口和Finalizer方法
class Test : IDisposable
{
Test()
{
//using some un managed resources like files or database connections.
}
~Test()
{
//since .NET garbage collector, does not call Dispose method, i call in Finalize method since .net garbage collector calls this
}
public void Dispose()
{
//release my files or database connections
}
}
Run Code Online (Sandbox Code Playgroud)
但是,我的问题是我为什么要这样做?
虽然根据我不能证明我的方法是合理的,但为什么我们在使用语句时可以使用IDisposable本身可以释放资源)
任何特定的优点或我在这里缺少一些东西?
在您的示例中使用语句是正确的,因为您仅在方法的范围内使用资源.例如:
Test1()
{
using (FileStream fs = new FileStream("c:\\test.txt", FileMode.Open))
{
byte[] bufer = new byte[256];
fs.Read(bufer, 0, 256);
}
}
Run Code Online (Sandbox Code Playgroud)
但是如果资源是在一个方法之外使用的,那么你应该创建Dispose方法.这段代码错了:
class Test1
{
FileStream fs;
Test1()
{
using (var fileStream = new FileStream("c:\\test.txt", FileMode.Open))
{
fs = fileStream;
}
}
public SomeMethod()
{
byte[] bufer = new byte[256];
fs.Read(bufer, 0, 256);
}
}
Run Code Online (Sandbox Code Playgroud)
要做的事情IDisposable
是确保文件在使用后将被释放.
class Test1 : IDisposable
{
FileStream fs;
Test1()
{
fs = new FileStream("c:\\test.txt", FileMode.Open);
}
public SomeMethod()
{
byte[] bufer = new byte[256];
fs.Read(bufer, 0, 256);
}
public void Dispose()
{
if(fs != null)
{
fs.Dispose();
fs = null;
}
}
}
Run Code Online (Sandbox Code Playgroud)
一张小纸条第一,因为你似乎有点困惑如何using
与IDisposable
彼此交互:你为什么能说的原因using (FileStream fileStream = Whatever()) { ... }
是,正是因为FileStream
类实现IDisposable
.你的同事曾建议是,你实现IDisposable
在你的类,这样你就可以说using (Test test = new Test()) { ... }
.
对于它的价值,我认为你最初编写代码的方式比建议的更改强烈,除非有一些令人信服的理由说明为什么你可能希望在实例FileStream
的整个生命周期中保持开放状态Test1
.可能出现这种情况的一个原因是,在调用构造函数之后,文件可能会从其他来源发生更改Test1
,在这种情况下,您将被困在较旧的数据副本中.保持FileStream
打开的另一个原因可能是,如果您特别想要在Test1
对象处于活动状态时将文件锁定从其他位置写入.
通常,最好尽快释放资源,这是您的原始代码所做的.我有点怀疑的一件事是,工作是在你的构造函数中完成的,而不是在某些从外部明确调用的方法中完成(解释:http://misko.hevery.com/code-reviewers-guide/flaw-构造函数 - 实际工作/).但这完全是一个不同的问题,并且与是否使你的类实现的问题无关IDisposable
.
“没有人”给出的答案是正确的,即该using
块只能用于实现该IDisposable
接口的类,并且对此的解释是完美的。您身边的问题是“为什么我需要在Test类上添加IDisposable,但是在代码审查时,却要求我在Test类上实现IDisposable接口和Finalizer方法。”
答案很简单
1)按照许多开发人员所遵循的编码标准,最好IDisposable
在使用一些资源的类上实现,并且一旦该对象的作用域超过了该类中的Dispose方法,就可以确保所有资源已被释放。
2)编写的类永远不会将来更改,并且如果进行了此类更改并添加了新资源,则开发人员知道他必须在Dispose函数中释放这些资源。
根据您提供的信息,绝对没有理由实施IDisposable
或终结Test
.
仅实现终结器以释放非托管资源(窗口句柄,GDI句柄,文件句柄).除非您正在使用Win32 API或其他东西,否则通常不必执行此操作.微软已经为您准备了这个,FileStream
所以您不必担心文件句柄.
终结器用于在对象被垃圾回收时清理非托管资源.
由于垃圾收集器在决定收集对象之前可能需要很长时间,因此您可能希望有一种方法来触发清理.不,GC.Collect()
这不是正确的方法.;)
为了允许尽早清理本机资源而不必等待垃圾收集器,您可以IDisposable
在类上实现.这使调用者可以在不等待GC的情况下触发清理.这并不会导致你的对象由GC来释放.它所做的一切都是免费的原生资源.
在对象拥有另一个Disposable对象的情况下,拥有对象也应该实现IDisposable
并简单地调用另一个对象Dispose()
.
例:
class Apple : IDisposable
{
HWND Core;
~Apple() { Free(); }
Free()
{
if(Core != null)
{
CloseHandle(Core);
Core = null;
}
}
Dispose() { Free(); }
}
class Tree : IDisposable
{
List<Apple> Apples;
Dispose()
{
foreach(var apple in Apples)
apple.Dispose();
}
}
Run Code Online (Sandbox Code Playgroud)
请注意,Tree
没有终结器.它实现Dispose
是因为它必须关心Apple
清理.Apple
有一个终结器,以确保它清理Core
资源.Apple
允许通过调用进行早期清理Dispose()
您不需要Dispose
并且当然不需要终结器的原因是因为您的类Test
不拥有任何不受管理的成员字段IDisposable
.你碰巧创造了一个FileStream
一次性的,但你在离开方法之前就把它清理干净了.它不归Test
对象所有.
这种情况有一个例外.如果您正在编写一个您知道将由其他人继承的类,而其他人可能必须实现IDisposable
,那么您应该继续实施IDisposable
.否则,调用者将不知道处置该对象(或者甚至能够在没有强制转换的情况下).然而,这是一种代码味道.通常,您不会从类继承并添加IDisposable
到它.如果你这样做,那可能是糟糕的设计.
归档时间: |
|
查看次数: |
13027 次 |
最近记录: |