我对一次性课程有一些疑问。假设我有一个IDisposable
实施类,有一些一次性成员。我已经实现了Dispose()
方法,即:
class BaseCustom: IDisposable
{
private System.Net.Sockets.TcpClient tc;
private System.Net.Sockets.NetworkStream ns;
public string str;
public int i;
public BaseCustom(string host, int port)
{
tc = new System.Net.Sockets.TcpClient(host, port);
ns = tc.GetStream();
}
// some other methods work on members (i, str, tc, ns)
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
if (ns != null)
{
ns.Close();
ns = null;
}
if (tc != null)
{
tc.Close();
tc = null;
}
}
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
}
Q1)由于没有非托管资源,是否可以压制终结器? (在此处的代码中阅读note)
Q2)由于我们处理了一次性成员并取消了GC,因此int
和string
成员会发生什么?我们还需要处理它们吗?
Q3) tc
和ns
当前正在释放吗?关于调用Close()
与Dispose()
现在假设我们有一个派生类:
class DerivedCustom : BaseCustom
{
public string cstr;
public int ci;
public DerivedCustom(string host, int port)
: base(host, port)
{}
// some extra methods
}
Q4)可能与Q2相关;我们需要覆盖任何Dispose()
吗?我们不在派生类中引入任何非托管或可处理资源。将其保留原样是否安全(即信任基地的处置机制)?如果GC被抑制,ci
和cstr
会发生什么情况(在派生的情况下也会被抑制,右边)?与Q1有关,我们还需要任何终结器吗?
Q5)如果基类是抽象类怎么办?
Q6)这很有趣;代码原样没有给出关于FxCop 1.36中可处置性的任何警告。但是,如果我将一次性成员添加到DerivedCustom
并且仍然不会覆盖可处置方法(因此不处理新成员),如:
class DerivedCustom : BaseCustom
{
public string cstr;
public int ci;
// below is the only extra line
public System.Net.Sockets.TcpClient ctc = new System.Net.Sockets.TcpClient("ho.st", 1234);
public DerivedCustom(string host, int port)
: base(host, port)
{}
// some extra methods
}
仍然没有在FxCop中收到任何警告。这有点让我感到惊讶,因为处理ctc
似乎没有得到妥善处理。
答案 0 :(得分:2)
A1)如果没有非托管资源,请不要在该类中添加终结器。终结器可用于清理非托管资源; GC将自行处理托管资源。
A2)SuppressFinalize不会阻止对象被垃圾收集;它只会阻止GC调用对象的终结器。未装箱的int永远不会被垃圾收集,因为它们是值类型;字符串将像往常一样被垃圾收集。
A3)我会调用Dispose,而不是Close,因为Dispose保证具有Dispose语义,而Close可能会略微不同。
A4)如果派生类没有添加新的处理要求,请不要覆盖Dispose方法。
A5)如果基础是抽象类,那就不会有什么不同。
A6)我对FXCop的回答不太了解,抱歉。
此外,没有理由将字段设置为null。例如,在没有非托管资源的情况下,Dispose方法应该如下所示(编辑:重新阅读问题中链接的FXCop页面,我想补充一下,在这种情况下应该密封类):
public void Dispose()
{
if (ns != null)
{
ns.Dispose();
}
if (tc != null)
{
tc.Dispose();
}
}
但是,如果类可能具有包含非托管资源的派生类型,那么您应该坚持使用经典模式,并对您的示例进行一些修改:
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
if (ns != null)
{
ns.Dispose();
}
if (tc != null)
{
tc.Dispose();
}
}
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
答案 1 :(得分:0)
Q1:是的。
Q2:不是。他们是100%托管类型,GC会照顾他们。 IDisposable
适用于继承或组合的某个类型,需要处理非托管资源。例如tc
和ns
。
问题3:致电Dispose()
。它是惯用的,一个好的实现会调用它自己的Close()
。
Dispose()
。
Q5:与Q4没有区别。
问题6:是的,FxCop在IDisposable
规则方面存在误报和漏报。遵循最佳实践,忽略FxCop看起来很可疑的东西,你会没事的。
答案 2 :(得分:0)
您应该考虑在实现IDisposable接口的基类中实现终结器( Dispose Pattern )。
IDisposable接口用于释放本机资源。 Int32
或String
由资源管理,由GC收集。
如果类使用非托管/本机资源,那么基类是抽象类还是具体类都没关系。
即使派生类没有定义任何新资源,但该类继承了基类功能,这意味着派生类对象将使用本机资源。即使您在派生类中跳过声明终结器但在基类中定义了终结器,基类终结器也足以指示GC在内存收集期间运行终结器。
可以(实际上必须)调用GC.SuppressFinalize
,因为它会从F-Reachable队列中删除条目,并帮助GC在第一次扫描中收集对象内存。