此类使用StreamWriter
,因此实现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;
}
}
目前的实施有什么问题吗?即,我是否必须手动释放基础FileStream
? Dispose(bool)
写得正确吗?
答案 0 :(得分:39)
如果您的类不直接使用非托管资源,则无需使用此广泛版本的IDisposable实现。
一个简单的
public virtual void Dispose()
{
_Writer.Dispose();
}
就足够了。
如果您的消费者无法处理您的对象,它将正常进行GC,而无需调用Dispose,_Writer持有的对象也将是GC并且它将有一个终结器,所以它仍然可以清理它的非托管资源得当。
修改强>
在对Matt和其他人提供的链接进行了一些研究后,我得出结论,我在这里的答案代表。原因如下: -
可继承类的一次性实现“模式”(我的意思是受保护的虚拟Dispose(bool),SuppressFinalize等marlarky)背后的前提是子类可能保持到一个非托管资源。
然而,在现实世界中,我们绝大多数的.NET开发人员从未接近非托管资源。如果你必须量化“可能”,那么你会想到什么样的.NET编码?
让我们假设我有一个Person类型(为了论证,在其中一个字段中有一个一次性类型,因此应该是一次性的)。现在我有继承者Customer,Employee等。如果有人继承了Person并希望拥有一个非托管资源,那么使用这个“模式”来混淆Person类是否合理?
有时候,我们开发人员可能会过度复杂化,试图对所有可能的情况进行编码,而不会对这种情况的相对概率使用一些常识。
如果我们想要直接使用非托管资源,那么合理的模式就是将这样的东西包装在自己的类中,其中完整的“一次性模式”是合理的。因此,在大量的“正常”代码中,我们不必担心所有这些问题。如果我们需要IDisposable,我们可以使用上面的简单模式,是否可继承。
p,很高兴从胸前把它弄下来。 ;)答案 1 :(得分:16)
您不需要Finalize
(析构函数)方法,因为您没有任何非托管对象。但是,如果从Foo继承的类具有非托管对象,那么您应该保持对GC.SuppressFinalize
的调用,因此是终结器。
如果你使类密封,那么你知道非托管对象永远不会输入等式,因此没有必要添加protected virtual Dispose(bool)
重载或GC.SuppressFinalize
。
编辑:
@ AnthonyWJones对此的反对意见是,如果您知道子类不会引用非托管对象,则不需要整个Dispose(bool)
和GC.SuppressFinalize
。但如果是这种情况,您应该真正创建类internal
而不是public
,Dispose()
方法应该是virtual
。如果您知道自己在做什么,那么请不要遵循Microsoft的建议模式,但在破坏规则之前,您应该了解并理解规则!
答案 2 :(得分:4)
建议的做法是仅在具有非托管资源(例如本机文件句柄,内存指针等)时使用终结器。
我有两个小建议,
没有必要
“m_Disposed
”变量用于测试是否
您之前曾致电Dispose
在您的托管资源上。你可以
使用类似的代码,
protected virtual void Dispose (bool disposing) { if (disposing) { if (_Writer != null) _Writer.Dispose (); } _Writer = null; }
只在必要时打开文件。因此,在您的示例中,您将使用File.Exists
在构造函数中测试文件是否存在,然后当您需要读取/写入文件时,然后您将打开它并使用它
此外,如果您只是想将文字写入文件,请查看File.WriteAllText
或File.OpenText
甚至File.AppendText
,其中的目标是专门针对{{1}的文本文件}。
除此之外,是的,您正在正确实现.NET ASCIIEncoding
模式。
答案 3 :(得分:3)
我有很多这样的课程 - 我的建议是尽可能让课程密封。 IDisposable
+继承可以工作,但99%的时候你不需要它 - 而且很容易犯错误。
除非你正在编写一个公共API(在这种情况下允许人们实现你的代码,但他们希望 - 即使用IDisposable
),你不需要支持它。
只是这样做:
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();
}
}
注意尝试... catch;从技术上讲,StreamWriter
构造函数可以抛出一个异常,在这种情况下,它永远不会拥有FileStream
的所有权,你必须自己处理它。
如果您真的使用了很多IDisposable
,请考虑将该代码放在C ++ / CLI中:它为您制作所有样板代码(它透明地为本机和托管对象使用适当的确定性破坏技术)
维基百科有一个适合C ++的IDisposable样本(实际上,如果你有很多IDisposable,C ++实际上很多比C#简单): Wikipedia: C++/CLI Finalizers and automatic variables
例如,在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!");
}
};
即使没有添加自定义IDisposable实现,此代码也会正确处理kidObj和kidObj2,并且它对于Dispose实现中的异常(不是那些应该发生但仍然存在)是健壮的,并且面对更多{而且很容易维护{1}}成员或本地资源。
并不是说我是C ++ / CLI的忠实粉丝,但是对于面向资源的大量代码来说,它很容易被C#击败,并且它与托管代码和本机代码完全互操作 - 简而言之,完美的胶水代码; - )。我倾向于在C#中编写90%的代码,但是使用C ++ / CLI来满足所有的互操作需求(特别是如果你想调用任何win32函数 - MarshalAs和其他所有互操作属性都是可怕的并且完全不可理解)。 / p>
答案 4 :(得分:1)
在尝试处置之前,您应该检查_Writer
是否不是null
。 (它似乎不可能是null
,但为了以防万一!)
protected virtual void Dispose(bool disposing)
{
if (!_Disposed)
{
if (disposing && (_Writer != null))
{
_Writer.Dispose();
}
_Writer = null;
_Disposed = true;
}
}
答案 5 :(得分:0)
如果你打开StreamWriter,你也必须处理它,否则你就会泄漏。
答案 6 :(得分:0)
我同意其他评论中所说的一切,但也会指出;
在任何一种情况下,您实际上都不需要设置_Writer = null。
如果你打算这样做,最好把它放在如果处置的地方。它可能不会产生很大的差异,但是你通常不应该在被终结者处理时使用托管对象(在这种情况下你不需要,正如其他人所指出的那样)。