首页 > 解决方案 > 未参考计时器框架 4.8 的对象未释放

问题描述

请看下面的例子。即使对 obj 的引用设置为 null,也不会释放 obj,并且 Obj_Elapsed 会继续打印 i。请注意,在 ObjectWithTimer 构造函数的范围之外没有对计时器的引用。

公共类程序{

    public static void Main(string[] args)
    {
        object obj = new ObjectWithTimer();
        Console.ReadLine();
        Console.WriteLine("obj=null");
        obj = null;
        Console.ReadLine();
    }
}

public class ObjectWithTimer
{
    private int i;
    public System.Timers.Timer t;
    public ObjectWithTimer()
    {
        t = new System.Timers.Timer(5000);
        t.Elapsed += Obj_Elapsed;
        t.Enabled = true;
    }

    public void Obj_Elapsed(object sender, ElapsedEventArgs e)
    {
        i++;
        Console.WriteLine(i);
    }
}

标签: c#timer

解决方案


在这种情况下设置null和/或超出范围是不够的,它Timer有它正在管理的资源并且需要清理。

由于System.Timers.TimerImplements IDisposable,理想情况下您的包装类也应该如此

public class ObjectWithTimer : IDisposable
{
   // Flag: Has Dispose already been called?
   private bool _disposed = false;

   private int _i;
   public System.Timers.Timer Timer { get; }
   public ObjectWithTimer()
   {
      Timer = new System.Timers.Timer(5000);
      Timer.Elapsed += Obj_Elapsed;
      Timer.Enabled = true;
   }

   public void Obj_Elapsed(object sender, ElapsedEventArgs e)
   {
      _i++;
      Console.WriteLine(_i);
   }

   // Public implementation of Dispose pattern callable by consumers.
   public void Dispose() =>Dispose(true);


   // Protected implementation of Dispose pattern.
   protected virtual void Dispose(bool disposing)
   {
      if (_disposed) return; 
      if (disposing) Timer?.Dispose();  
      _disposed = true;
   }
}

然后,您还应该处理在某个阶段调用的包装器,而不仅仅是将其设置为null,最简单的方法是使用 using 语句

提供一种方便的语法,确保正确使用 IDisposable 对象。

public static void Main(string[] args)
{
    using(object obj = new ObjectWithTimer())
    {
       Console.ReadLine();
       Console.WriteLine("obj=null");
    }
    Console.ReadLine();
}

实现 Dispose 方法

您实现一个 Dispose 方法来释放应用程序使用的非托管资源。.NET 垃圾收集器不分配或释放非托管内存。

注意:这不是关于该IDisposable模式的完整教程,只是一个示例。请对此实施进行自己的研究和尽职调查


额外资源


推荐阅读