来自具有一次性成员的一次性类别的类别

时间:2012-10-12 16:35:28

标签: c# garbage-collection idisposable tcpclient

我对一次性课程有一些疑问。假设我有一个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,因此intstring成员会发生什么?我们还需要处理它们吗?

Q3) tcns当前正在释放吗?关于调用Close()Dispose()

的版本,我看到.NET引用中的差异

现在假设我们有一个派生类:

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被抑制,cicstr会发生什么情况(在派生的情况下也会被抑制,右边)?与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似乎没有得到妥善处理。

3 个答案:

答案 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适用于继承或组合的某个类型,需要处理非托管资源。例如tcns

问题3:致电Dispose()。它是惯用的,一个好的实现会调用它自己的Close()

第四季:不,没有必要的改写。您无法抑制GC - 您禁止GC运行终结器,因为您已经运行了Dispose()

Q5:与Q4没有区别。

问题6:是的,FxCop在IDisposable规则方面存在误报和漏报。遵循最佳实践,忽略FxCop看起来很可疑的东西,你会没事的。

答案 2 :(得分:0)

您应该考虑在实现IDisposable接口的基类中实现终结器( Dispose Pattern )。

IDisposable接口用于释放本机资源。 Int32String由资源管理,由GC收集。

如果类使用非托管/本机资源,那么基类是抽象类还是具体类都没关系。

即使派生类没有定义任何新资源,但该类继承了基类功能,这意味着派生类对象将使用本机资源。即使您在派生类中跳过声明终结器但在基类中定义了终结器,基类终结器也足以指示GC在内存收集期间运行终结器。

可以(实际上必须)调用GC.SuppressFinalize,因为它会从F-Reachable队列中删除条目,并帮助GC在第一次扫描中收集对象内存。