自定义控件MyControl中,设置了一个定时器
因为你的定时器是设置在控件中的,所以无法回收
而且你其实开了2个定时器

解决方案 »

  1.   

    要想控件能够被回收,你的控件必须没有任何地方引用它
    任何地方,至少包含以下3个地方
    1.没有变量对它强引用
    2.没有添加到窗体控件容器中
    3.没有任何事件会去不断调用里面的方法
    你现在的问题就是3在保持对它的引用,timer回调事件不停的去执行实例中的方法,导致实例无法被回收
      

  2.   

    以上3的说法其实有点错误
    其实是你的控件里new了一个timer,而这个timer一直在工作,在timer被回收之前,控件本身也无法被回收
    除非控件里的对象都被回收,控件自身才有可能被回收
      

  3.   

    是你的控件还没来得及回收,你可以在不用的时候禁用timer
      

  4.   

    那如果MyControl控件中没有定时器,那new第二个MyControl的时候,第一个MyControl是不是立即被销毁了呢?销毁与否跟计时器有关系吗?
      

  5.   

    我测试了一下,至少在1分钟之内,是不断弹出"MyControl1"、"MyControl2"的,分钟之后,我就没测试了,还不知道会不会继续弹出"MyControl1"、"MyControl2"。
    如果是控件还没来得及回收,难道说这么长的时间,都还没有回收吗?甚至有可能是一直都不会回收....
      

  6.   

    我测试了一下,至少在1分钟之内,是不断弹出"MyControl1"、"MyControl2"的,分钟之后,我就没测试了,还不知道会不会继续弹出"MyControl1"、"MyControl2"。
    如果是控件还没来得及回收,难道说这么长的时间,都还没有回收吗?甚至有可能是一直都不会回收....
      

  7.   

    那如果MyControl控件中没有定时器,那new第二个MyControl的时候,第一个MyControl是不是立即被销毁了呢?销毁与否跟计时器有关系吗?
    不会立即回收,GC会负责定期,或内存吃紧的时候,回收.
    前提是我在3楼说的,没有地方引用这个东西,GC判断它已经没用了,才会被回收
    你的控件里有个timer一直在工作,GC怎么知道你到底是想让它工作还是不想让它工作了?
      

  8.   

    这个跟DispatcherTimer的实现有关系。1、DispatcherTimer触发的事件需要发生在UI线程上。
    2、因此,它使用了Dispatcher.CurrentDispatcher来运行触发过程,并在Start中把自己添加到Dispatcher.CurrentDispatcher中去。
    3、就是说Dispatcher.CurrentDispatcher引用了DispatcherTimer。
    4、而DispatcherTimer.Tick引用了MyControl的成员函数timer_Tick。
    5、成员函数delegate引用了MyControl实例对象。Dispatcher.CurrentDispatcher(属于当前的UI线程 )不会被回收,它引用的DispatcherTimer就不会被回收,DispatcherTimer引用的MyControl就不会被回收。注:上述第2点可以参考源代码(目前是第287行),而DispatcherTimer.Stop则会把自己同Dispatcher.CurrentDispatcher断开。
    http://referencesource.microsoft.com/#WindowsBase/src/Base/System/Windows/Threading/DispatcherTimer.cs
      

  9.   

    所以如果你的窗口关闭了,不用担心timer不会被回收,也不需要在关闭前手动将timer停止
      

  10.   

    你在给Content 赋值之前应该先判断其值是否为空,如果不为空就先停止timer,然后再重新赋值        private void button2_Click(object sender, RoutedEventArgs e)
            {
                MyControl mc = button2.Content as MyControl;
                if (mc == null)
                {
                    button2.Content = new MyControl() { Name = "MyControl1" };
                }
                else
                {
                    mc.timer.Stop();
                    button2.Content = new MyControl() { Name = "MyControl1" };
                }            mc = button2.Content as MyControl;
                if (mc == null)
                {
                    button2.Content = new MyControl() { Name = "MyControl2" };
                }
                else
                {
                    mc.timer.Stop();
                    button2.Content = new MyControl() { Name = "MyControl2" };
                }
            }
        }    public class MyControl : Control
        {
            public DispatcherTimer timer = new DispatcherTimer();
            static MyControl()
            {
                DefaultStyleKeyProperty.OverrideMetadata(typeof(MyControl), new FrameworkPropertyMetadata(typeof(MyControl)));
            }
            public MyControl()
            {
                timer.Interval = new TimeSpan(0, 0, 2);
                timer.Tick += new EventHandler(timer_Tick);
                timer.Start();
            }
            private void timer_Tick(object sender, EventArgs e)
            {
                MessageBox.Show(this.Name);
            }
        }自己把代码简化一下
      

  11.   

    .net的回收不具备即时性,也就是说你可以调用dispose()方法把对象标记为无效引用的对象,然后由GC来回收,但是什么时候回收却是由回收机制决定的
      

  12.   

    那你13楼写的代码,可不可以不写呢,在new新的MyControl之前,直接将先前的MyControl调用dispose()方法,这样就Timer就不会再运行了吧,更简洁吧
      

  13.   

    那你13楼写的代码,可不可以不写呢,在new新的MyControl之前,直接将先前的MyControl调用dispose()方法,这样不就回收了吗,更简洁吧