2

我有一些关于一次性课程的问题。假设我有一个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)既然没有非托管资源,可以压制finalizer吗?(在此处阅读代码中的注释

Q2)既然我们处理了一次性成员并压制了 GC,那么这里的成员intstring成员会发生什么?我们是否也需要处理它们?

Q3)电流释放是否tc合适ns?我看到不同版本的 .NET 引用中关于调用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()s 吗?我们不会在派生类中引入任何非托管或一次性资源。保持原样是否安全(即相信基地的处置机制)?如果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似乎没有得到妥善处理。

4

3 回答 3

2

A1) 如果没有非托管资源,请不要在类中添加终结器。终结器对于清理非托管资源很有用;GC 将自行处理托管资源。

A2) SuppressFinalize 不会阻止对象被垃圾收集;它只会阻止 GC 调用对象的终结器。未装箱的整数永远不会被垃圾收集,因为它们是值类型;字符串将像往常一样被垃圾收集。

A3) 我会调用 Dispose,而不是 Close,因为 Dispose 保证具有 Dispose 语义,而 Close 可能会做一些稍微不同的事情。

A4)如果派生类没有增加新的dispose要求,不要重写Dispose方法。

A5) 如果基类是抽象类,那也没什么区别。

A6) 我对 FXCop 的了解不够,无法回答,抱歉。

此外,没有理由将字段设置为空。例如,在没有非托管资源的情况下,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);         
}         
于 2012-10-12T16:43:43.960 回答
0

Q1:是的。

Q2:不。它们是 100% 托管类型,GC 会处理它们。IDisposable适用于沿继承或组合线某处需要处置非托管资源的类型。比如tcns

Q3:打电话Dispose()。这是惯用的,一个好的实现会调用它自己的Close().

Q4:不,不需要覆盖。你在哪里抑制 GC - 你正在抑制 GC 运行终结器,因为你已经运行了Dispose().

Q5:与 Q4 没有区别。

Q6:是的,FxCop 的规则有误报和漏报IDisposable。遵循最佳实践并忽略 FxCop 中看似可疑的内容,您会没事的。

于 2012-10-12T16:45:32.523 回答
0

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

IDisposable 接口用于释放本机资源。Int32或被String管理资源并由 GC 收集。

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

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

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

于 2012-10-12T16:46:23.457 回答