C# 我是否正确实施了 IDisposable?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1136210/
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
Am I implementing IDisposable correctly?
提问by mafu
This class uses a StreamWriter
and therefore implements IDisposable
.
此类使用 aStreamWriter
并因此实现IDisposable
.
public class Foo : IDisposable
{
private StreamWriter _Writer;
public Foo (String path)
{
// here happens something along the lines of:
FileStream fileWrite = File.Open (path, FileMode.OpenOrCreate, FileAccess.Write, FileShare.ReadWrite);
_Writer = new StreamWriter (fileWrite, new ASCIIEncoding ());
}
public void Dispose ()
{
Dispose (true);
GC.SuppressFinalize (this);
}
~Foo()
{
Dispose (false);
}
protected virtual void Dispose (bool disposing)
{
if (_Disposed) {
return;
}
if (disposing) {
_Writer.Dispose ();
}
_Writer = null;
_Disposed = true;
}
private bool _Disposed;
}
}
}
Is there any issue with the current implementation? I.e., do I have to release the underlying FileStream
manually? Is Dispose(bool)
written correctly?
当前的实现有什么问题吗?即,我是否必须FileStream
手动释放底层?被Dispose(bool)
正确写入?
采纳答案by AnthonyWJones
You don't need to use this extensive version of IDisposable implementation if your class doesn't directly use unmanaged resources.
如果您的类不直接使用非托管资源,则不需要使用此扩展版本的 IDisposable 实现。
A simple
一个简单的
public virtual void Dispose()
{
_Writer.Dispose();
}
will suffice.
就足够了。
If your consumer fails to Dispose your object it will be GC'd normally without a call to Dispose, the object held by _Writer will also be GC'd and it will have a finaliser so it still gets to clean up its unmanaged resources properly.
如果您的使用者未能处理您的对象,它通常会在不调用 Dispose 的情况下被 GC,_Writer 持有的对象也将被 GC 处理,并且它会有一个终结器,因此它仍然可以正确清理其非托管资源。
Edit
编辑
Having done some research on the links provided by Matt and others I've come to the conclusion that my answer here stands. Here is why:-
已经做了由Matt提供的链接和其他人,我得出的结论,我的答案在这里的一些研究站。原因如下:-
The premise behind the disposable implementation "pattern" (by that I mean the protected virtual Dispose(bool), SuppressFinalize etc. marlarky) on an inheritable class is that a sub-class mighthold on to an unmanaged resource.
可继承类上的一次性实现“模式”(我的意思是受保护的虚拟 Dispose(bool)、SuppressFinalize 等 marlarky)背后的前提是子类可能保留非托管资源。
However in the real world the vast majority of us .NET developers never go anywhere near an unmanaged resource. If you had to quantify the "might" above what probabilty figure would you come up with for you sort of .NET coding?
然而,在现实世界中,我们绝大多数 .NET 开发人员从未接近非托管资源。如果您必须量化高于什么概率数字的“可能”,您会为您的 .NET 编码想出什么?
Lets assume I have a Person type (which for sake of argument has a disposable type in one of its fields and hence ought to be disposable itself). Now I have inheritors Customer, Employee etc. Is it really reasonable for me to clutter the Person class with this "Pattern" just in case someone inherits Person and wants to hold an unmanaged resource?
让我们假设我有一个 Person 类型(为了论证,它的一个字段中有一个一次性类型,因此它本身应该是一次性的)。现在我有继承人 Customer、Employee 等。如果有人继承 Person 并想要持有一个非托管资源,我用这个“模式”混淆 Person 类真的合理吗?
Sometimes we developers can over complicate things in an attempt to code for all possible circumstances without using some common sense regarding the relative probability of such circumstances.
有时,我们的开发人员可能会将事情复杂化,试图为所有可能的情况编写代码,而不使用有关此类情况的相对概率的一些常识。
If we ever wanted to use an unmanaged resource directly the sensible pattern would be wrap such a thing in its own class where the full "disposable pattern" would be reasonable. Hence in the significantly large body of "normal" code we do not to have to worry about all that mucking about. If we need IDisposable we can use the simple pattern above, inheritable or not.
如果我们想直接使用非托管资源,明智的模式将把这样的东西包装在它自己的类中,其中完整的“一次性模式”是合理的。因此,在大量“正常”代码中,我们不必担心所有这些乱七八糟的东西。如果我们需要 IDisposable 我们可以使用上面的简单模式,无论是否可以继承。
Phew, glad to get that off my chest. ;)
呼,很高兴能把它从我的胸膛上拿开。;)
回答by Tal Pressman
If you open the StreamWriter, you have to Dispose it as well, or you'll have a leak.
如果你打开StreamWriter,你也必须Dispose它,否则你会泄漏。
回答by Matt Howells
You don't need a Finalize
(destructor) method since you don't have any unmanaged objects. However, you should keep the call to GC.SuppressFinalize
in case a class inheriting from Foo has unmanaged objects, and therefore a finalizer.
您不需要Finalize
(析构函数)方法,因为您没有任何非托管对象。但是,您应该保持调用GC.SuppressFinalize
,以防从 Foo 继承的类具有非托管对象,因此是终结器。
If you make the class sealed, then you know that unmanaged objects are never going to enter the equation, so it isn't necessary to add the protected virtual Dispose(bool)
overload, or the GC.SuppressFinalize
.
如果您使类密封,那么您知道非托管对象永远不会进入等式,因此没有必要添加protected virtual Dispose(bool)
重载或GC.SuppressFinalize
.
Edit:
编辑:
@AnthonyWJones's objection to this is that if you know that the subclasses will not reference unmanaged objects, the whole Dispose(bool)
and GC.SuppressFinalize
is unnecessary. But if this is the case you should really make the classes internal
rather than public
, and the Dispose()
method should be virtual
. If you know what you are doing, then feel free not to follow Microsoft's suggested pattern, but you should know and understand the rules before you break them!
@AnthonyWJones 对此的反对意见是,如果您知道子类不会引用非托管对象,那么整个Dispose(bool)
和GC.SuppressFinalize
都是不必要的。但如果是这种情况,您应该真正制作类internal
而不是public
,并且Dispose()
方法应该是virtual
. 如果您知道自己在做什么,那么请不要遵循 Microsoft 的建议模式,但您应该在违反规则之前了解并理解规则!
回答by Mike J
The recommended practice is to use a finalizer only when you have unmanaged resources (such as native file handles, memory pointers etc).
推荐的做法是仅当您拥有非托管资源(例如本机文件句柄、内存指针等)时才使用终结器。
I have two small suggestions though,
不过我有两个小建议,
It's not necessary to have the
"m_Disposed
" variable to test if
you have previously called Dispose
on your managed resources. You could
use similar code like so,
m_Disposed
如果您以前调用Dispose
过托管资源,则没有必要使用“ ” 变量来测试。你可以使用类似的代码,
protected virtual void Dispose (bool disposing) { if (disposing) { if (_Writer != null) _Writer.Dispose (); } _Writer = null; }
protected virtual void Dispose (bool disposing) { if (disposing) { if (_Writer != null) _Writer.Dispose (); } _Writer = null; }
Open files for only as long as is necessary. So in your example, you would test for the existence of the file in the constructor using File.Exists
and then when you need to read/write the file, thenyou would open it and use it.
仅在需要时打开文件。因此,在你的榜样,你会测试中使用构造该文件的存在File.Exists
,然后当你需要读/写文件,那么你会打开它,使用它。
Also, if you merely weant to write text to a file, have a look at File.WriteAllText
or File.OpenText
or even File.AppendText
which is aimed at text files specifically with ASCIIEncoding
.
另外,如果你只是weant写文本文件,看看File.WriteAllText
或者File.OpenText
甚至File.AppendText
其目的是在文本文件专门与ASCIIEncoding
。
Other than that, yes you are implementing the .NET Dispose
pattern correctly.
除此之外,是的,您正在Dispose
正确实现 .NET模式。
回答by LukeH
You should check that _Writer
isn't null
before attempting to dispose it. (It doesn't seem likely that it ever will be null
, but just in case!)
在尝试处理它之前,您应该检查它_Writer
不是null
。(它似乎永远不会是null
,但以防万一!)
protected virtual void Dispose(bool disposing)
{
if (!_Disposed)
{
if (disposing && (_Writer != null))
{
_Writer.Dispose();
}
_Writer = null;
_Disposed = true;
}
}
回答by Yort
I agree with everything said in the other comments, but would also point out that;
我同意其他评论中所说的一切,但也要指出;
You don't actually need to set _Writer = null in either case.
If you're going to do so, it's probably best to put it inside the if where the dispose is. It probably doesn't make a big differece, but you're generally not supposed to play with managed objects when being disposed by the finaliser (which you don't need in this case anyway, as others have pointed out).
在任何一种情况下,您实际上都不需要设置 _Writer = null。
如果您打算这样做,最好将其放在处理所在的 if 中。它可能不会产生太大的不同,但是当被终结器处理时,您通常不应该使用托管对象(在这种情况下无论如何您都不需要,正如其他人所指出的那样)。
回答by Eamon Nerbonne
I have many classes like this - and my recommendation is to make the class sealed whenever possible. IDisposable
+ inheritance can work, but 99% of the times you don't need it - and it's easy to make mistakes with.
我有很多这样的课程 - 我的建议是尽可能让课程密封。 IDisposable
+ 继承可以工作,但在 99% 的情况下您不需要它 - 而且很容易出错。
Unless you're writing a public API (in which case it's nice to permit people to implement your code however they wish to - i.e. to use IDisposable
), you don't need to support it.
除非您正在编写公共 API(在这种情况下,允许人们以他们希望的方式实现您的代码是很好的 - 即使用IDisposable
),您不需要支持它。
just do:
做就是了:
public sealed class Foo : IDisposable
{
private StreamWriter _Writer;
public Foo(string path)
{
FileStream fileWrite = File.Open (path, FileMode.OpenOrCreate, FileAccess.Write, FileShare.ReadWrite);
try {
_Writer = new StreamWriter (fileWrite, new ASCIIEncoding());
} catch {
fileWrite.Dispose();
throw;
}
}
public void Dispose()
{
_Writer.Dispose();
}
}
Note the try...catch; technically the StreamWriter
constructor could throw an exception, in which case it never takes ownership of the FileStream
and you must dispose it yourself.
注意 try...catch; 从技术上讲,StreamWriter
构造函数可能会抛出异常,在这种情况下,它永远不会获得 的所有权,FileStream
您必须自己处理它。
If you're really using many IDisposable
s, consider putting that code in C++/CLI: that makes all that boilerplate code for you (it transparently uses the appropriate deterministic destruction tech for both native and managed objects).
如果您确实使用了许多IDisposable
s,请考虑将该代码放在 C++/CLI 中:这将为您生成所有样板代码(它透明地对本机和托管对象使用适当的确定性销毁技术)。
Wikipedia has a decent IDisposable sample for C++ (really, if you have many IDisposable's, C++ is actually muchsimpler than C#): Wikipedia: C++/CLI Finalizers and automatic variables.
维基百科有对C体面IDisposable的样品++(真的,如果你有很多的IDisposable的,C ++实际上是多比C#简单): 维基百科:C ++ / CLI终结和自动变量。
For example, implementing a "safe" disposable container in C++/CLI looks like...
例如,在 C++/CLI 中实现一个“安全”的一次性容器看起来像......
public ref class MyDisposableContainer
{
auto_handle<IDisposable> kidObj;
auto_handle<IDisposable> kidObj2;
public:
MyDisposableContainer(IDisposable^ a,IDisposable^ b)
: kidObj(a), kidObj2(b)
{
Console::WriteLine("look ma, no destructor!");
}
};
This code will correctly dispose kidObj and kidObj2 even without adding a custom IDisposable implementation, and it's robust to exceptions in either Dispose implementation (not that those should occur, but still), and simple to maintain in the face of many more IDisposable
members or native resources.
即使不添加自定义 IDisposable 实现,此代码也将正确处理 KidObj 和 KidObj2,并且它对任一 Dispose 实现中的异常都很健壮(不是应该发生,但仍然存在),并且在面对更多IDisposable
成员或本机资源时易于维护.
Not that I'm a huge fan of C++/CLI, but for heavily resource-oriented code it's got C# beat easily, and it has absolutely brilliant interop both with managed and native code - in short, perfect glue code ;-). I tend to write 90% of my code in C#, but use C++/CLI for all interop needs (esp. if you want to call any win32 function - MarshalAs and other interop attributes all over the place are horrifying and thoroughly incomprehensible).
并不是说我是 C++/CLI 的忠实粉丝,但是对于大量面向资源的代码,它很容易击败 C#,并且它与托管代码和本机代码都具有绝对出色的互操作性 - 简而言之,完美的胶水代码 ;-)。我倾向于用 C# 编写 90% 的代码,但使用 C++/CLI 来满足所有互操作需求(尤其是如果你想调用任何 win32 函数 - MarshalAs 和其他互操作属性到处都是可怕的和完全无法理解的)。