【问题标题】:C# Destructor not calling after out of scopeC#析构函数在超出范围后不调用
【发布时间】:2012-06-14 15:35:37
【问题描述】:

超出范围后析构函数出现问题(它正在调用但经过一段时间后需要对表单执行操作,例如更改单选按钮),可能是我的代码有错误。看看:

namespace WindowsFormsApplication2
{
    public partial class Form1 : Form
    {

        public Form1()
        {
            InitializeComponent();
            EventLogger.Print += delegate(string output)
            { if (!textBox1.IsDisposed) this.Invoke(new MethodInvoker(() => textBox1.AppendText(output + Environment.NewLine)), null); };
        }

        private void button1_Click(object sender, EventArgs e)
        {
            TestClass test = new TestClass();
        }
    }
    public static class EventLogger
    {
        public delegate void EventHandler(string output);
        public static event EventHandler Print;
        public static void AddLog(String TextEvent)
        {
            Print(TextEvent);
        }
    }
    public class TestClass
    {
        public TestClass()
        {
            EventLogger.AddLog("TestClass()");
        }
        ~TestClass()
        {
            EventLogger.AddLog("~TestClass()");
        }
    }

}

【问题讨论】:

  • 我建议你使用 IDisposable 模式而不是析构函数。特别是如果您没有操作系统级别的句柄。你不应该使用析构函数。
  • 永远不要依赖析构函数。
  • 在另一张纸条上。你有问题吗?析构函数在 GC 时被调用,这是你无法控制的。您也不能强制 GC 进行清理。无论如何,您甚至都不需要代码中的任何析构函数。

标签: c# scope destructor


【解决方案1】:

对,因为这不是 C++。终结器(不是 C++ 中的析构函数)不能保证在对象离开其声明范围后立即被调用,它会在 GC 决定突然介入并在你之后清理时调用。

请问您为什么一开始就使用终结器?您是否维护对需要尽可能确定地释放的非托管资源的引用(如果是,请在 IDisposable 接口上阅读)? C# 终结器的用例很少而且相差甚远,实现它们并不常见。

【讨论】:

  • 当我不使用表单应用程序而是使用控制台时,一切正常。
  • @user1112008 你的代码不需要析构函数。为什么你认为你需要它?
  • 我只需要自动化 UI。每个类都包含在表单上的列表视图中作为文本。当它创建时,它从构造函数添加到列表视图,然后超出范围(析构函数),它从列表视图中删除。对不起我的英语。
  • @user1112008 这在使用 RAII 的 C++ 中是惯用的,但该模式不能直接转移到 C#。您在 C# 中最接近的是“使用模式”,但您可能希望完全以不同的方式构建事物。由于析构函数,RAII 实际上是 C++ 特有的模式。
【解决方案2】:

C# 不是 C++。 Destructors don't run synchronously

您的代码中没有任何错误,但看起来您可能需要为您的类实现“IDisposable 模式”,以便为调用者提供一种方法来保证您的对象的某些破坏是同步执行的。

【讨论】:

    【解决方案3】:

    终结器并非设计为在超出范围后立即调用。它在对象被垃圾收集时调用,可能是超出范围后的几毫秒到几天。

    终结器不适用于此类代码。仅用于资源清理。

    您不能在超出范围后立即强制它执行某些操作,但您可以在超出范围之前立即使用Close() 或类似方法来指示对象已完成使用。

    例如:

    private void button1_Click(object sender, EventArgs e)
    {
        TestClass test = new TestClass();
        // do stuff
        test.Close();
    }
    

    注意:您可以按照建议实现IDisposable,但这种用法并不完全符合IDisposable 的预期用途,因此虽然它可以工作,但它有点hackish。

    【讨论】:

    • 是的,但是将它放入析构函数的美妙之处在于(在 C++ 中)只要对象超出范围就会调用它。这意味着任何时候你从函数返回,析构函数都会被调用。这样您就不必在函数中的每个 return 语句之前调用 test.Close()
    【解决方案4】:

    对于我们这些更熟悉 C++ 模式的人来说,查看 IDisposable 接口的文档很有用:

    http://msdn.microsoft.com/en-us/library/system.idisposable.aspx

    不幸的是,我刚刚尝试过它并没有按我的意愿工作。我使用了一个自动对象来保存 GUI 光标的当前状态,切换到等待光标并在对象超出范围时恢复原始光标。 IDisposable 界面确实会导致光标被恢复但不是立即恢复 - 因此等待光标显示时间过长。太糟糕了,因为这是一个非常有用的模式。

    更新:一段时间后,C# try/finally 模式已经足够舒适了:

    public void do_something_time_consuming()
    {
       ShowBusyCursor cursor = new ShowBusyCursor();
    
       try
       {
          ...
          return;
       }
       finally
       {
          cursor.done();
       }
    }
    

    【讨论】:

      猜你喜欢
      • 2012-09-26
      • 1970-01-01
      • 1970-01-01
      • 2018-09-15
      • 2016-09-26
      • 2013-10-20
      相关资源
      最近更新 更多