【问题标题】:Cannot access a disposed object C# (showdialog dispose)无法访问已处置的对象 C# (showdialog dispose)
【发布时间】:2017-07-06 01:40:10
【问题描述】:

我是 c# 的新手,并且有点喜欢它。使用 Microsoft Visual C# 2010

我查看了许多类似的帖子,但似乎没有任何建议有帮助

我收到以下错误:“无法访问已处置的对象” 这里引用了主窗体

private void btn_RunPkgs_Click(object sender, EventArgs e)
        {
            RunPackages rp = new RunPackages();    
            this.Hide();
            rp.ShowDialog();//The error points to this line
            this.Show();
        }

这里是安全检查失败时崩溃的代码。

private void securityCheck()
        {
            if (MyGlobals.FormCheck("RUN_JOBS") == 1)
            {
                InitializeComponent();
            }
            else
            {
                //this.BeginInvoke(new MethodInvoker(this.Close));
                //this.DialogResult = System.Windows.Forms.DialogResult.Cancel;
                MessageBox.Show("You do not have permission to access this form!");
                //this.Close();
                this.Dispose();
            }            
        }

编辑 看起来我会接受 Adriano Repetti 的想法,即把安全性放在我称之为页面的地方,但我现在有点紧张,因为页面上有任何安全性。

   private void btn_RunPkgs_Click(object sender, EventArgs e)
        {
            if (MyGlobals.FormCheck("RUN_JOBS") == 1)
            {
                RunPackages rp = new RunPackages();
                this.Hide();
                rp.ShowDialog();
                this.Show();                
            }
            else
            {
                MessageBox.Show("Not for You!");             
            }
        }

        private void btn_ListUpdater_Click(object sender, EventArgs e)
        {
            if (MyGlobals.FormCheck("MDM") == 1)
            {
                ListUpdater lu = new ListUpdater();

                this.Hide();
                lu.ShowDialog();
                this.Show();
            }
            else
            {
                MessageBox.Show("Private!");
            }            
        }

EDIT2 想出了以下可能的解决方案,但使用它时很紧张,因为我是新手,不知道可能存在什么问题。为表单加载创建事件处理程序有什么问题吗?

namespace RunPackages
{
    public partial class ListUpdater : Form
    {
        public ListUpdater()
        {            
            InitializeComponent();
            this.Load += new EventHandler(securityCheck);
        }    

        private void securityCheck(object sender, EventArgs e)
        {
            if (MyGlobals.FormCheck("MDM1") == 0)
            {
                MessageBox.Show("Not Allowed!");
                this.Close();
            }    
        }

【问题讨论】:

  • this.Dispose() 你想处理什么?你为什么这么叫?
  • 我写了一个安全检查,如果用户在该特定表单上失败,我希望它显示一个错误消息框并关闭该表单,返回主表单。我第一次尝试这个。关闭
  • 那是因为您在 ShowDialog 期间是表单的 Disposing,因为在此之后它无法引用自己,所以猜测您没有向我们展示更多代码。跨度>
  • 一般来说,在进行最基本的编码时,不需要直接处理对象。把这种事情留给垃圾收集器。为什么this.Close() 被注释掉了?似乎这应该足以满足您的需求。
  • 对每个表单进行安全检查(不同的表单有不同的权限) MyGlobals.Formcheck("FormName") 检查用户和表单的权限。如果权限很好,它会很好地工作,当用户检查失败时它会关闭表单,这是一个问题。

标签: c# .net winforms


【解决方案1】:

您不能在表单本身中处理表单。 ShowDialog() 方法尝试在退出时访问表单以获取诸如 DialogResult 之类的内容。

【讨论】:

    【解决方案2】:

    在一个表单被释放后,几乎所有的方法都不能被访问(并且它的大部分属性都是无效的)。

    btn_RunPkgs_Click() 的第一行中,您创建了一个对象并将其放置在其构造函数中。 本身,即使你可能在构造函数中调用Dispose() 的习惯非常糟糕,它甚至可能会起作用,但是你尝试使用这样的对象ShowDialog() 会生成ObjectDisposedException。请注意,此代码也会导致相同的结果(异常):

    RunPackages rp = new RunPackages();
    rp.Dispose();
    

    是的,你可以检查IsDisposed,但这不会使代码可读,问题(IMO)是你在混合东西。构造函数不应该包含这样的逻辑。

    重点不只是在哪里您处理表单。更好的是甚至不创建这样的表单(让我假设,因为你调用InitializeComponent()securityCheck() 在表单构造函数中被调用),为此你可以使用工厂静态方法:

    public static bool TryShowDialog(Form currentForm)
    {
        if (MyGlobals.FormCheck("RUN_JOBS") != 1)
            return false;
    
        if (currentForm != null)
            currentForm.Hide();
    
        RunPackages dlg = new RunPackages();
        dlg.ShowDialog();   
    
        if (currentForm != null)
            currentForm.Show();
    
        return true;
    }
    

    你的调用函数将被简化为:

    private void btn_RunPkgs_Click(object sender, EventArgs e)
    {
        RunPackages.TryShowDialog(this);
    }
    

    请注意,此类函数非常适合进行某些重构(例如提取代码以隐藏/显示现有表单)。像这样的:

    public static bool ShowDialog<T>(Form currentForm, string authorizationId)
        where T : Form, new()
    {
        if (MyGlobals.FormCheck(authorizationId) != 1)
            return false;
    
        if (currentForm != null)
            currentForm.Hide();
    
        T dlg = new T();
        T.ShowDialog();   
    
        if (currentForm != null)
            currentForm.Show();
    
        return true;
    }
    

    这样使用(现在代码到处都可以重用):

        SecurityHelpers.ShowDialog<RunPackages>(this, "RUN_JOBS");
    

    请注意,调用代码可能会被简化(例如authorizationId 可能是RunPackages 上的一个属性,并且currentForm 也可以从当前活动形式中推导出来)。

    EDIT 调用Close() 并不好,如果没有创建窗口句柄(让我们稍微简化一下:它是在显示窗口时创建的),它会在内部调用Dispose()(然后以上适用)。

    【讨论】:

    • 我以前每个页面都有安全功能,我把它变成了一个全局方法/类/事物(我是新手,不知道术语)。我当然可以将安全性移至调用页面。我会试一试,让你知道。仍然不确定为什么 this.Close 不起作用。
    • @Lazer Close() 不起作用,因为...窗口尚未可见。您仍在构造函数中(顺便说一句,它不应该包含任何逻辑,但需要创建对象本身)
    • 我有点担心目标页面上没有任何安全性,但这个想法对我有用。最终让它变得更加简单。感谢您简单地解释为什么 Close() 和 Dispose() 不起作用。
    • 这并不意味着您不应该在表单中进行安全检查。它是Debug.Assert() 的一个很好的候选对象,它甚至可以嵌入到AuthorizationRequiredForm 基类中。这样的检查在发布时是相当多余的,但在调试期间绝对是至关重要的(它可以放在OnShow 方法中)。
    【解决方案3】:

    我不会尝试破坏导致表单创建的事件链。
    副作用很难预测,今天有效的方法在未来的版本中也无效。

    相反,我会尝试不同的方法

        private void securityCheck()
        {
            if (MyGlobals.FormCheck("RUN_JOBS") == 1)
            {
                InitializeComponent();
            }
            else
            {
                Label message = new Label();
                message.Dock = DockStile.Fill;
                message.Text("You do not have permission to access this form!.");
                message.TextAlign = ContentAlignment.MiddleCenter;
                this.Controls.Add(message);
            }      
        }
    

    通过这种方式,我让表单只显示一个标签,该标签覆盖了整个表单表面和您的消息。用户只能关闭表单(前提是您没有移除控制框)

    顺便说一句,这样做的好处是避免了危险的疏忽,因为它不需要对调用代码进行任何更改,最终效果是有效地阻止了表单的使用。

    如果您坚持在其构造阶段关闭表单,那么您可以从this question获得一些建议

    【讨论】:

    • +1 很酷的解决方案,但我在 else 中不包含 InitializeComponent() 时遇到了一些问题。链接也很有用,但最终选择了 Adriano Repetti 的回答
    【解决方案4】:

    我想出了以下内容,谁能告诉我这是否有任何问题?

    namespace RunPackages
    {
        public partial class ListUpdater : Form
        {
    
            public ListUpdater()
            {            
                InitializeComponent();
                this.Load += new EventHandler(securityCheck);
            }    
    
            private void securityCheck(object sender, EventArgs e)
            {
                if (MyGlobals.FormCheck("MDM1") == 0)
                {
                    MessageBox.Show("Not allowed!");
                    this.Close();
                }    
            }
    

    等等……

    【讨论】:

    • 无论如何,我都会将此合并到问题中(或者-更好-将其作为新问题发布在 CodeReview 上)...您不需要附加事件处理程序,您可以简单地覆盖 OnLoad 方法。此外,如果它被广泛使用,您可以将其移至基类(实际上,您知道,我会将其完全移到您的表单之外,但这是另一回事......)
    【解决方案5】:

    使用标志。例如更改代码,如下所示:

        public bool IsDisposed;
        private void securityCheck()
                {
                    if (MyGlobals.FormCheck("RUN_JOBS") == 1)
                    {
                        InitializeComponent();
                    }
                    else
                    {
                        //this.BeginInvoke(new MethodInvoker(this.Close));
                        //this.DialogResult = System.Windows.Forms.DialogResult.Cancel;
                        MessageBox.Show("You do not have permission to access this form!");
                        //this.Close();
                        this.Dispose();
                        this.IsDisposed = true;
                    }      
    
    
            }
    

    然后:

    private void btn_RunPkgs_Click(object sender, EventArgs e)
            {
                RunPackages rp = new RunPackages();    
                if(rp.IsDisposed)
                    return;
                this.Hide();
                rp.ShowDialog();//The error points to this line
                this.Show();
            }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-11-04
      • 2011-10-23
      • 2011-03-25
      • 1970-01-01
      相关资源
      最近更新 更多