无法访问已释放对象错误

10

在我关闭表格后,当我尝试重新打开它时,我遇到了错误信息"Cannot access a disposed object. Object name: 'ApplicationProperties'."。我注意到这是因为退出表格时,程序正在"销毁"表格,所以我已经在所有的确认按钮和取消按钮(任何会关闭一个表格的按钮)中加入了以下代码。

 this.Hide(); 
 this.Parent = null;

这段代码只是隐藏了表单,并没有关闭表单

我的问题在于,当我点击表单上的“x”按钮后,再尝试重新打开表单时,仍然会收到错误消息。我尝试了几种修改表单现有函数的方法,例如:

    private void ApplicationProperties_FormClosing(object sender, FormClosingEventArgs e)
    {
        //Hiding the window, because closing it makes the window unaccessible.
        this.Hide();
        this.Parent = null;
    }

但是这没带给我任何好运。我想知道是否有人知道如何解决这个问题。以下是在取消和接受按钮内对我有效的代码。它对于所有关闭表单的按钮都是相同的。

    private void OptionsCancelbtn_Click(object sender, EventArgs e)
    {
        //Hiding the window, because closing it makes the window unaccessible.
        this.Hide();
        this.Parent = null;
    }

我已经在form1的顶部声明了一个实例,并且在form1内有一个按钮可以打开form2。

public partial class MainBox : Form
{
    //Making a name for the ApplicationProperties form. It can be opened when called.
    ApplicationProperties ApplicationPropertiesWindow = new ApplicationProperties();
    private void ApplicationPropertiesbtn_Click(object sender, EventArgs e)
    {
        //Show the properties window.
        ApplicationPropertiesWindow.Show();
    }//End ApplicationProperties button.
 }

当我用第二个窗体上的'x'按钮关闭程序后,由于错误消息在ApplicationPropertiesWindow.Show();处触发,我无法再次访问form2。

在form2内部,我有以下代码:

public partial class ApplicationProperties : Form
{
    //Creates and sets the instance MainBoxWindow.
    public MainBox MainBoxWindow { get; set; }

1
你能展示一下在问题开头描述的那个出现错误的代码吗?那个才是真正需要修复的,而不是你试图绕过它的尝试。 - Cody Gray
期望的行为是什么?您要么尝试访问已经处理的ApplicationProperties对象,要么在代码中无意中处理它。要么初始化一个新的ApplicationProperties对象,要么找到您正在处理它的位置并删除代码,如果您希望该对象保留在内存中。检查类的定义,或在Dispose上执行查找。 - B L
我正在尝试访问该对象,但当我点击“x”按钮时,它被释放了。 - Chase Ernst
哦,对不起。通过那个表单,还保存了其他值到一个 .xml 文件中。所以我认为如果它被处理掉了,那应该没问题,我可以每次打开表单时加载 .xml 文件。另外,我搜索了我的代码,没有找到 Dispose。 - Chase Ernst
1
在这种情况下,每当您想重新加载它时,只需初始化一个新的ApplicationsProperty对象可能就可以了。我纯粹是猜测您是否尝试使用相同的标识符,因为我没有看到您在上面初始化表单的地方,但如果您向我们展示代码的这一部分,我们可以帮助您解决问题。 - B L
显示剩余3条评论
4个回答

6

在你的 FormClosing 事件中尝试这个:

  private void ApplicationProperties_FormClosing(object sender, FormClosingEventArgs e)
  {
        //Hiding the window, because closing it makes the window unaccessible.
        this.Hide();
        this.Parent = null;
        e.Cancel = true; //hides the form, cancels closing event
   }

因为现在它仍然关闭表单,因此仍会导致错误。


我尝试了一下,但没有运气。 - Chase Ernst
有没有其他正在运行但我们没有看到的东西? - Hanlet Escaño
我可能错了,但关闭表单不是导致问题的原因吗?我需要隐藏它吗?我不是100%确定,但我认为那就是发生的事情。 - Chase Ernst
我不知道还有什么程序在运行。我会添加更多信息来更新这个问题。 - Chase Ernst
e.Cancel可以防止窗体关闭。this.Parent = null是在做什么? - Hanlet Escaño
这是我老板的代码,我正在尝试扩展他的程序,因为它在按钮上运作良好,所以我将其转移到退出函数。即使我将其注释掉,它仍然无法工作。 - Chase Ernst

5
当我使用第二个窗体的“x”按钮关闭程序后,由于在ApplicationPropertiesWindow.Show();处触发了错误消息,我无法再次访问form2。
当一个窗体被关闭(Form.Close),窗体本身和所有相关资源都会被释放。文档中指出有两个特殊情况除外:
  1. 该窗体是MDI应用程序的一部分且不可见。
  2. 该窗体使用ShowDialog(而非Show)显示为模态对话框。这样设计是为了在用户关闭对话框后可以访问对话框的属性(例如检索用户输入)。
在这两种特殊情况下,您需要手动调用窗体的Dispose方法。第二种情况是最常见的(现在几乎没有人再使用MDI范例),并且可以通过using语句轻松处理:
using (MyDialogBox dlg = new MyDialogBox())
{
    DialogResult result = dlg.ShowModal(this);
    if (result == DialogResult.Yes)
    {
        // access members of "dlg", and
        // do whatever the user asked
    }
}  // the Dispose method is automatically called here

在您的情况下,与典型情况一样,调用 Close 方法将关闭并销毁窗体。您已经知道无法访问已释放的对象(因为它不再存在!),所以当您尝试显示它时会出现异常。如果要在关闭后再次显示窗体,则需要创建该窗体类的新实例:
MyForm frm = new MyForm();
frm.Show();
// ...
frm.Close();

这真的是最好的方法。新的表单实例将与您关闭的表单完全相同,因为它是从相同的类创建的。最好开始以面向对象的方式思考,并尽可能避免基于单例的设计。出现在屏幕上的每个表单都是一个新的、独立的对象。一旦该对象关闭并销毁,您就无法再使用它。如果您想再次使用它,需要创建一个新对象并显示它。 Hide方法更像是一种hack,仅在您希望暂时隐藏表单的当前实例同时仍保留其状态(例如,其成员属性的值、其控件状态等)时有用。这仅适用于永远不会被销毁的单例对象,并且必须经过精心设计和维护。这也意味着该表单对象始终消耗资源,无论是否正在使用,这是浪费的。
如果你必须这样做,你需要找出导致表单实例被释放的原因。除非我能看到你的所有代码,否则我只能猜测问题可能出在哪里。这可能与你使用的AcceptButton和/或CancelButton属性有关。无论如何,最好和最干净的解决方案是重写OnFormClosing方法并防止你的窗体被关闭。相反地,你应该将其隐藏起来:
public class MyForm : Form
{
    protected virtual void OnFormClosing(FormClosingEventArgs e)
    {
        // Prevent the form from closing.
        e.Cancel = true;

        // Hide it instead.
        this.Hide();
    }

    // ...other code in your form class
}    

这样做的好处在于,您只需将代码放在一个地方,即负责类的本地位置,而不是暴露给外部代码并分散在应用程序中。当然,它还可以防止表单被框架代码关闭,而这些代码超出了您的控制范围。
我不知道为什么要将“Parent”属性设置为“null”。顶级窗口(所有窗体都是如此)永远不能有父级。只有子窗口(例如控件)才有父级。它可以有一个所有者,但不一定会有。这取决于您在调用“Show”方法时是否传递所有者窗口作为参数。

3

首先,移除 this.Parent=null,因为在隐藏表单时不需要它。

现在,当你隐藏表单并且仍然想要访问该表单时,请将表单存储在静态变量中。因为当对象不再使用时,垃圾收集器会处理它,并且它将不再可用。


我该如何处理? - Chase Ernst
我正在用我的手机回答这个问题。因此,请考虑到如果我没有立即回复。 - sourabh devpura
我该如何将表单存储在静态变量中? - Chase Ernst
这与我已经在两个表单之间拥有的实例发生了冲突。 - Chase Ernst
创建一个单独的静态公共类,并将此变量放入该类中,或者如果您有多个窗体,请使用更多的变量。 - sourabh devpura
显示剩余3条评论

3
问题可能是一部分类的相互依赖。当您初始化一个ApplicationProperties实例时,您创建了对MainBox对象的引用,但在MainBox类的定义中,您创建了一个新的ApplicationsProperties对象,该对象引用了MainBox……我甚至把自己都搞糊涂了。MainBox是您的父窗体吗?即当您启动应用程序时加载的那个窗体?
如果您在ApplicationProperites类中设置对MainBox的引用,那么我不会感到惊讶,因为当您单击“X”时,这个类将被处理掉,有些MainBox中的内容也可能会无意中被销毁。再次声明,这只是我的猜测;我不确定这是否是原因,但即使代码本身没有问题,对我来说看起来也很奇怪。
如果您希望您的ApplicationProperties窗口可以持续存在并简化问题,只需在任何要持续应用程序的表单的开始处初始化其静态实例,并类似于您最初尝试的那样显示/隐藏它。如果您想要高效和基础,只需在需要让用户修改它时构建和处理ApplicationProperties即可,但请消除循环依赖关系。

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