什么是方法变量,可以匿名方法访问,垃圾收集?

时间:2011-02-09 07:40:44

标签: c# garbage-collection anonymous-methods

例如是否有必要将Timer实例添加到列表中,因为我在这里做以防止Timer被垃圾回收?如果回调不是匿名的话,那么aswer是肯定的,但是它是匿名的我想象在匿名方法块中可以访问的方法块中的变量只会在匿名方法完成时被垃圾收集?在这种情况下,不需要像我一样保存ref ..:

private static List<Timer> timers = new List<Timer>();

public static void RunCallbackAfter(Action callback, int secondsToWait)
{
        Timer t = null;
        t = new Timer(new TimerCallback(delegate(object state)
            {
                SomeThread.BeginInvoke(callback);
                timers.Remove(t);
            }), null, secondsToWait*1000, Timeout.Infinite);
        timers.Add(t);
}

3 个答案:

答案 0 :(得分:2)

匿名方法中捕获的变量引用的对象将无法进行垃圾回收,直到匿名方法创建的委托符合垃圾回收的条件。

但是,如果只有Timer引用了该委托,并且其他任何内容都没有引用该计时器,那么我怀疑这两个都有资格进行垃圾收集,假设这确实是你需要保持引用的那种计时器。 (我似乎记得一些计时器需要这个,而一些不要。我不记得哪个是哪个。)

此外,如果您从匿名方法中删除了timers.Remove(t)调用,那么它首先不会捕获t。它只是捕获的变量,它们的生命周期延长了......而不是包含匿名方法的方法中的每个变量。

答案 1 :(得分:0)

没有。你不需要藏匿你的计时器。它通常是垃圾收集,因为它只由您的代表引用。但是,我相信Timer构造函数会为底层运行时放置一个引用,所以你应该没问题。

Eric Lippert在他的博客文章中可能有话要说:The implementation of anonymous methods in C# and its consequences (part 1)

只要存在对您的匿名方法的引用,您的Timer变量t就可以继续访问。在事件触发之前,您的匿名方法将保持引用状态。至少那么久。

根据Eric Lippert的说法,c#编译器将你的代码转换成其他东西,你的方法的上下文(包括封闭对象的this指针)全部包含在它自己的小编译器生成的类中。所以似乎匿名方法(或委托)本身包含对计时器的引用。

哦,这个帖子中的其他人都是正确的:我只是脱口而出(并且了解了C#编译器如何同时处理匿名方法)。

所以是的,你有一个循环引用。但我很确定创建一个计时器应该在运行时/窗口的某个地方挂钩。我们来看看。

使用反射器,您可以跟踪从System.Timer.Timer()TimerBase的路径,该路径具有外部方法AddTimerNative。我不确定如何查看,但我打算用操作系统注册你的计时器。

结论:您的计时器不会超出范围,因为操作系统会引用它直到它触发。

答案 2 :(得分:0)

一般情况下,C#中的匿名函数会保留所有引用的局部变量,直到anon函数本身被销毁为止。当然,在这种情况下删除remove调用会删除引用,这意味着回调将不再保留变量。

但是,这里形成一个循环参考;除非有外部参考,否则计时器和回调可能会同时被破坏。我不确定启动计时器是否算作外部参考,使其在C#中保持活着,所以我无法完全回答你的问题。如果一个已启动的计时器被视为具有外部引用,那么仅此一项将使计时器和回调保持活动状态。