1

我的框架中有几个对象,根据要求需要提供事件 ObjectTerminated。框架的用户可以订阅此事件并清理他正在使用的一些非托管内容。这些对象被设计为在应用程序的整个生命周期中都存在,我不会控制它们的生命周期。您可以将它们视为单例数组。

我想写这样的代码:

class SomeWorkflowControlObject
{
     public event EventHandler<> ObjectTerminated;

     ~SomeWorkflowControlObject()
     {
          if (ObjectTerminated != null) ObjectTerminated(this, null);         
     }
}

我不确定,我是否允许这样做。这种解决方案可能会出现什么问题?

更新:

Process.GetCurrentProcess().Exited 怎么样?我可以以这种方式使用它吗?

4

2 回答 2

1

你不应该这样做。基本上,C# 中不存在析构函数。您编写的是终结器,终结器唯一应该做的就是释放非托管资源。

根本不允许您访问任何其他托管对象,因为垃圾收集器可能已经将其删除。我认为您的null支票不足以防范这种情况;该对象引用可能仍指向您的(事件)委托,即使它已经消失了。

所以简而言之,不要这样做。

备择方案:

  1. 如果您有 Windows 窗体应用程序,请订阅该Application.ApplicationExit事件。

  2. 您可能需要考虑实现IDisposable接口,然后执行以下操作:

    public class SomethingVeryLongLived : IDisposable
    {
        …
    }
    
    …
    
    public static void Main()
    {
        using (var sth = new SomethingVeryLongLived(…))
        {
            Application.Run(new SomeForm(…));
        } // <-- at this point, foo.Dispose() is guaranteed to be called.
    }
    

    请注意,即使在 using 的情况下,IDisposable触发正在释放的对象内部的事件可能也不是一个好主意/设计,因为不应再访问已释放的对象。

    出于这个原因,我建议您执行以下操作:

  3. 使用try…finally

    public static void Main()
    {
        var sth = new SomethingVeryLongLived(…);
        try
        {
            Application.Run(new SomeForm(…));
        }
        finally
        {
            SomethingVeryLongLived.Terminate();
        }
    }
    

    这对我来说似乎是最好的,因为您没有滥用IDisposable界面,而且代码的作用非常清楚......没有隐藏的含义。

于 2012-07-06T12:47:35.550 回答
1

我认为该框架的设计存在固有缺陷。让我解释:

1. [...] 我们框架中的几个对象,根据要求需要提供 event ObjectTerminated

这没有意义。如果一个对象已被终止,正如事件的名称所暗示的那样,那么我会假设它已经消失并且我无法再访问它。这提出了两个问题:

  • 死了的东西如何触发事件?就像一具尸体在坟墓里对你说:“我死了。” 你真的想要这个吗?

  • 如果事件发送者不再应该在那里,为什么其他人会对这样的事件做出反应?尸体埋了之后还有什么要清理的?

2. 我没有控制他们的生活。

那么谁控制他们的一生呢?为什么他们没有责任在适当的时候进行或触发必要的清理工作?让我进一步详细说明这一点:

3. [...] 可以订阅此事件并清理一些非托管的东西 [...]

这个非托管的东西在哪里,哪个对象负责处理它?如果它是您自己的对象,那么为什么您的对象不处置它 - 为什么您要触发一个事件,以便其他人可以处置这些东西?这就像我在搬运邻居的垃圾,而不是他自己做。(我不是在说那里的老太太。)

如果它看起来像这样,您的课程会更有意义:

class SomeWorkflowControlObject : IDisposable
{
     // the following event doesn't make sense, sorry.
     // public event EventHandler<> ObjectTerminated;

     private IntPtr _handleToUnmanagedResource;

     ~SomeWorkflowControlObject()
     {
         Dispose(explicitly: false);         
     }

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

     protected virtual void Dispose(bool explicitly)
     {
         if (explicitly)
         {
             // free managed resources here; perhaps trigger an event 'Disposing'.
         }
         DisposeUnmanagedResource(_handleToUnmanagedResource);
     }
}

也就是说,它是一些非托管资源的包装器,它自己负责处理这些资源,而不是其他人。因此,不再需要触发事件,以便其他人可以处置非托管资源,无论如何这些资源都应该隐藏在您的对象中。

于 2012-07-06T13:50:31.377 回答