使用匿名委托进行事件处理时的垃圾收集 [英] Garbage collection when using anonymous delegates for event handling

查看:15
本文介绍了使用匿名委托进行事件处理时的垃圾收集的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

更新

我已将此处的各种答案组合成 新问题.

I have combined various answers from here into a 'definitive' answer on a new question.

原始问题

在我的代码中,我有一个事件发布者,它在应用程序的整个生命周期都存在(这里简化为基本要素):

In my code I have an event publisher, which exists for the whole lifetime of the application (here reduced to bare essentials):

public class Publisher
{
    //ValueEventArgs<T> inherits from EventArgs
    public event EventHandler<ValueEventArgs<bool>> EnabledChanged; 
}

因为这个发布者可以在任何地方使用,我很高兴自己创建了这个小助手类以避免在所有订阅者中重写处理代码:

Because this publisher can be used all over the place, I was quite pleased with myself for creating this little helper class to avoid re-writing the handling code in all subscribers:

public static class Linker
{
    public static void Link(Publisher publisher, Control subscriber)
    {
         publisher.EnabledChanged += (s, e) => subscriber.Enabled = e.Value;
    }

    //(Non-lambda version, if you're not comfortable with lambdas)
    public static void Link(Publisher publisher, Control subscriber)
    {
         publisher.EnabledChanged +=
             delegate(object sender, ValueEventArgs<bool> e)
             {
                  subscriber.Enabled = e.Value;
             };
    }
}

它工作得很好,直到我们开始在较小的机器上使用它,当我开始偶尔:

It worked fine, until we started using it on smaller machines, when I started getting the occasional:

System.ComponentModel.Win32Exception
Not enough storage is available to process this command

事实证明,在代码中的某个地方,订阅者控件是动态创建、添加和从表单中删除的.鉴于我对垃圾收集等方面的深入了解(即直到昨天还没有),我从来没有想过要在我身后进行清理,因为在绝大多数情况下,订阅者也会在应用程序的整个生命周期内都存在.

As it turns out, there is one place in the code where subscribers controls are being dynamically created, added and removed from a form. Given my advanced understanding of garbage collection etc (i.e. none, until yesterday), I never thought to clear up behind me, as in the vast majority of cases, the subscribers also live for the lifetime of the application.

我已经用 Dustin 摆弄了一段时间Campbell 的 WeakEventHandler,但它不适用于匿名代表(反正不适合我).

I've fiddled around a while with Dustin Campbell's WeakEventHandler, but it doesn't work with anonymous delegates (not for me anyway).

有没有解决这个问题?我真的很想避免在整个商店复制粘贴样板代码.

Is there anyway out of this problem? I really would like to avoid having to copy-paste boiler-plate code all over the shop.

(哦,别问我为什么我们一直在创建和销毁控件,这不是我的设计决定......)

(Oh, and don't bother with asking me WHY we are creating and destroying controls all the time, it wasn't my design decision...)

(PS:这是一个winforms应用程序,但我们已经升级到VS2008和.Net 3.5,我应该考虑使用弱事件模式?)

(PS: It's a winforms application, but we've upgraded to VS2008 and .Net 3.5, should I consider using the Weak Event pattern?)

(PPS:很好的 Rory 的回答,但是如果有人能想出一个与 WeakEventHandler 等效的方法,那我就不用记得明确取消链接/处置,那会很酷...)

(PPS: Good answer from Rory, but if anyone can come up with an equivalent to the WeakEventHandler which avoids me having to remember to explicitly UnLink/Dispose, that would be cool...)

编辑我必须承认我通过回收"有问题的控件来解决这个问题.然而,解决方法又回来困扰我,因为我使用的钥匙"显然不是唯一的(呜咽).我刚刚发现了其他链接here(试过这个 - 似乎是有点 too 弱 - 即使目标仍然存在,GC 也会清除委托,与 s,oɔɯǝɹ 答案),here (强制你修改发布者,并且实际上不适用于匿名代表)和 这里(引用为不完整的达斯汀坎贝尔).

EDIT I must admit that I worked around this problem by "recycling" the controls in question. However the workaround has come back to haunt me as the 'key' I was using is apparently non-unique (sob). I've just discovered other links here (tried this - seems to be a bit too weak - GC clears delegates even if target is still alive, same problem with s,oɔɯǝɹ answer below), here (forces you to modify publisher, and doesn't really work with anonymous delegates) and here (cited-as-incomplete by Dustin Campbell).

我突然想到,我正在寻找的东西在语义上可能是不可能的——闭包的设计目的是即使在我离开后也能闲逛".

It occurs to me that what I'm looking for may be semantically impossible - closures are designed to 'hang around even after I'm gone'.

我找到了另一种解决方法,所以我会坚持下去,等待来自众神的声音.

I've found another workaround, so I'll stick with that, pending a voice from the gods.

推荐答案

我知道这个问题很古老,但见鬼——我找到了它,而且我认为其他人也可以.我正在尝试解决相关问题,并且可能会有一些见解.

I know that this question is ancient, but hell - I found it, and I figure that others might as well. I'm trying to resolve a related issue, and might have some insight.

您提到了 Dustin Campbell 的 WeakEventHandler - 它确实不能通过设计使用匿名方法.当我意识到 a) 在 99% 的情况下我需要这样的东西时,我试图把一些东西摆在一起,他原来的解决方案会更安全,并且 b) 在我必须的少数情况下(注意:有to,而不是想要,因为 lambdas 更漂亮和简洁")如果你有点聪明,它可以让它工作.

You mentioned Dustin Campbell's WeakEventHandler - it indeed cannot work with anonymous methods by design. I was trying to fiddle something together that would, when I realized that a) in 99% of cases I'd need something like this his original solution would be safer, and b) in those few cases where I have to (note: have to, not "want to because lambdas are so much prettier and concise") it's possible to make it work if you get a little clever.

您的示例似乎完全是一种一次性的案例,稍微复杂一点就可以得到一个相当简洁的解决方案.

Your example seems like exactly the kind of one-off case where getting a little tricky can result in a fairly concise solution.


public static class Linker {
    public static void Link(Publisher publisher, Control subscriber) {
        // anonymous method references the subscriber only through weak 
        // references,so its existance doesn't interfere with garbage collection
        var subscriber_weak_ref = new WeakReference(subscriber);

        // this instance variable will stay in memory as long as the  anonymous
        // method holds a reference to it we declare and initialize  it to 
        // reserve the memory (also,  compiler complains about uninitialized
        // variable otherwise)
        EventHandler<ValueEventArgs<bool>> handler = null;

        // when the handler is created it will grab references to the  local 
        // variables used within, keeping them in memory after the function 
        // scope ends
        handler = delegate(object sender, ValueEventArgs<bool> e) {
            var subscriber_strong_ref = subscriber_weak_ref.Target as Control;

            if (subscriber_strong_ref != null) 
                subscriber_strong_ref.Enabled = e.Value;
            else {
                // unsubscribing the delegate from within itself is risky, but
                // because only one instance exists and nobody else has a
                // reference to it we can do this
                ((Publisher)sender).EnabledChanged -= handler;

                // by assigning the original instance variable pointer to null
                // we make sure that nothing else references the anonymous
                // method and it can be collected. After this, the weak
                //  reference and the handler pointer itselfwill be eligible for
                // collection as well.
                handler = null; 
            }
        };

        publisher.EnabledChanged += handler;
    }
}

据传 WPF 弱事件模式会带来很多开销,因此在这种特殊情况下我不会使用它.此外,在 WinForm 应用程序中引用核心 WPF 库似乎也有点繁重.

The WPF Weak Event pattern is rumored to come with a lot of overhead, so in this particular situation I wouldn't use it. Furthermore, referencing the core WPF library in a WinForm app seems a little heavy as well.

这篇关于使用匿名委托进行事件处理时的垃圾收集的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆