如何在Windows窗体上使用IDisposable模式

9

我在这篇文章中读到了IDisposable模式的相关内容,并想要在我的Windows窗体应用程序中实现它。我们知道,在Windows窗体.Designer.cs类中已经有Dispose方法。

private System.ComponentModel.IContainer components = null;

protected override void Dispose(bool disposing)
{
    if (disposing && (components != null))
    {
        components.Dispose();
    }
    base.Dispose(disposing);
}

在.cs类中,我使用了Typed Dataset来读取和保存数据。

public partial class frmCustomerList
{
    private MyTypedDataSet ds = new MyTypedDataSet();
    ...
}

那么,如何实现IDisposable来释放MyTypedDataSet?如果在frmCustomerList中实现IDisposable并实现它的接口。
public partial class frmCustomerList : IDisposable
{
    private MyTypedDataSet ds = new MyTypedDataSet();
    void Dispose()
    {
       ds.Dispose();
    }
}

在 .Designer.cs 文件中,Dispose(bool disposing) 方法是怎么样的呢?


为什么需要处理 MyTypedDataSet - Ben Aaronson
我认为你不需要在表单上使用 IDisposable。除非你持有大量未经管理的内存,否则你不需要将其处理掉。 - Ben N
你为什么想在一个窗体上实现 IDisposable 接口呢?你不觉得只需要响应 FormClosed 事件来进行清理就可以了吗? - Enigmativity
@Enigmativity 是的,我可以在 FormClosed 中清理任何资源(DataSet),但我只是想知道什么是适当的方式? - Willy
1
@Willy - FormClosed(或FormClosing)是适当的方式。我不建议更改设计器代码以使IDisposable起作用。 - Enigmativity
显示剩余2条评论
3个回答

14

如果你查看 Designer.cs 文件并在 dispose 方法下方 往下 看,你会看到这个:


    protected override void Dispose(bool disposing)
    {
        if (disposing && (components != null))
        {
            components.Dispose();
        }
        base.Dispose(disposing);
    }

    #region Windows Form Designer generated code

    /// <summary>
    /// Required method for Designer support - do not modify
    /// the contents of this method with the code editor.
    /// </summary>
    private void InitializeComponent()
    {

只有InializeComponent()被警告不要进行修改。您可以剪切(而非复制)并将protected override void Dispose(bool disposing)从设计文件中移出,然后将其移入您的主代码文件中,而不必担心,只需确保将components.Dispose(); 部分留下来,因为您通过设计器添加的任何可处理对象都将放入该集合以供处理。

public partial class frmCustomerList
{
    private MyTypedDataSet ds = new MyTypedDataSet();

    protected override void Dispose(bool disposing)
    {
        ds.Dispose();

        if (disposing && (components != null))
        {
            components.Dispose();
        }
        base.Dispose(disposing);
    }

    //The rest of your frmCustomerList.cs file.
}

这是一个非常糟糕的想法。更改设计师生成的代码不是一个好主意,而是一个非常糟糕的主意。 - Mick
2
@Mick Visual Studio被设计用于处理这种更改。我永远不会建议在#region Windows Form Designer generated code区域内更改任何内容,这就是为什么他们将Dispose()函数放在该区域之外的原因。 - Scott Chamberlain
无论文件中的区域如何,我仍不会去修改 Designer.cs,该文件由设计器生成。 对文件的更改很容易丢失,并且还有其他方法可以解决此问题,而不需要编辑该文件。 - Mick
2
这对我来说是更好的答案,因为我不需要抑制CA2213。点赞。谢谢Scott。 - Biscuits
2
这应该是被接受的答案... 这是实际上正确的方法。 - Nyerguds

4

嗨,@Mick,为什么你使用protected override void OnClosed(EventArgs e)而不是private void frmCustomerList_FormClosed(object sender, FormClosedEventArgs e)?有什么顾虑吗? - Willy
你可以使用任何一种方式。在功能上没有区别。订阅关闭事件实际上可能更安全,因为你可能会忘记调用基类方法,这会导致问题。 - Mick
1
只要表单处于打开状态,这个方法就能正常工作。一个表单可以被创建(即通过其构造函数),而不必显示出来。 - Guillermo Prandi

0

我认为除非你有非托管资源,否则你不必担心处理你的类。以下是一个实际有用的例子:

public class ComplexResourceHolder : IDisposable
{

    private IntPtr buffer; // unmanaged memory buffer
    private SafeHandle resource; // disposable handle to a resource

    public IntPtr Buffer { get { return buffer; } set { buffer = value; } }

    public ComplexResourceHolder()
    {
        this.buffer = ... // allocates memory
        this.resource = ... // allocates the resource
    }

    protected virtual void Dispose(bool disposing)
    {
        ReleaseBuffer(buffer); // release unmanaged memory
    if (disposing)
    { 
        // release other disposable objects
        if (resource!= null)
           resource.Dispose();
    }
}

~ ComplexResourceHolder(){
    Dispose(false);
}

public void Dispose(){
    Dispose(true);
    GC.SuppressFinalize(this);
}

}

查看MSDN以更好地理解Dispose(bool)终结器重写。此外,此链接关于非托管资源也将非常有用,因为这是您应该使用IDisposable的第一个原因。

如果类继承了IDisposable,您可能会想要使用以下构造:

using (ComplexResourceHolder crh = new ComplexResourceHolder())
{
    //Do something with buffer for an instance
    //crh.Buffer =
}

在关闭标记“}”后,Dispose方法将自动调用。


3
这是误导性的。如果类的成员实现了dispose,你应该在类中实现dispose,而不仅仅是在类直接处理非托管资源时才实现。即使对于托管资源,类通常也会实现dispose以确保及时回收昂贵的托管资源。 - Mick
大多数.NET类不实现IDisposable接口。为什么?因为它是不必要的。 - Wallstrider
1
所有来自 System.Windows.Forms 的控件都实现了 IDisposable 接口。这就是为什么所有的窗体都包含 Dispose 方法的原因。 - Wallstrider
我们正在平行宇宙中进行编程。如果我看到一个实例化了IDispose接口的对象,而拥有该对象的对象从未调用该对象的Dispose方法,或者它没有被包装在using语句中,那么在我的书中这是一个错误。我甚至会进一步提到GC需要帮助垃圾回收,特别是在像Windows Phone和Windows CE这样的平台上。实现Dispose和解耦对象(将变量设置为null)确实会产生影响。循环依赖关系可能会创建永远不会被收集的类块。 - Mick
1
我同意你上面的两篇帖子。但是我已经尝试具体地回答这个话题了。@Willy 没有提到他想要处理哪个类,使用什么平台等信息。所以因此我认为没必要实现 IDisposable。我的观点只是当你实际上存在内存泄漏等问题时,你必须使用 Dispose,但这并不意味着你需要为每个类实现 IDisposable。 :D - Wallstrider
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接