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

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

问题描述

更新



我将各种各样的答案从这里组合成一个 > 原始问题



在我的代码中,我有一个事件发布者,它存在于应用程序的整个生命周期中(这里减少到裸机) :

  public class Publisher 
{
// ValueEventArgs< T>继承自EventArgs
public event EventHandler< ValueEventArgs< bool>> EnabledChanged;
}

由于这个发布商可以在这个地方使用,我很高兴我自己创建这个小帮手类,以避免在所有订阅者中重新编写处理代码:

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

//(非lambda版本,如果你不适合lambdas)
public static void Link(发布者发布者,控制订阅者)
{
publisher.EnabledChanged + =
delegate(object sender,ValueEventArgs< bool> e)
{
subscriber.Enabled = e.Value;
};
}
}

它工作正常,直到我们开始在较小的机器,当我开始偶尔:

  System.ComponentModel.Win32Exception 
没有足够的存储可用来处理这个命令

事实证明,在动态创建订阅者控件的代码中有一个地方,从表单中添加和删除。鉴于我对垃圾收集等的高级理解(即没有一个,直到昨天),我从来没有想过要清除我的身份,因为在绝大多数情况下,订阅者也可以在一生中使用。



我已经在一段时间内与 Dustin Campbell的WeakEventHandler ,但它不适用于匿名代理人(不是我的)。



有没有这个问题?我真的很想避免在商店里复制粘贴锅炉版代码。



(哦,不要问我为什么我们正在创建并且总是破坏控制,这不是我的设计决定...)



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



(PPS:Good 来自Rory的回答,但如果有人可以来相当于WeakEventHandler,这避免了我不得不记住明确的UnLink / Dispose,这将是很酷...)



编辑我必须承认,我通过回收有关的控制措施来解决这个问题。然而,解决方法已经回到困扰我,因为我使用的关键显然是非唯一的(sob)。我刚刚发现了其他链接 here (试过 - 似乎是一个bit too weak - GC清除代理,即使目标仍然存在,与 s,oɔɯǝɹanswer ),这里(迫使您修改发布商,并且不能真正与匿名代理人协同工作)和这里(被Dustin Campbell引用为不完整)。



对我来说,我正在寻找的内容可能在语义上是不可能的 - 闭包旨在挂起,即使我走了。



我发现了另一种解决方法,所以我会坚持下去,等待一个来自神的声音

解决方案

我知道这个问题是古老的,但是地狱 - 我发现了,我认为其他人也可以。我想解决一个相关的问题,可能有一些洞察力。



你提到了Dustin Campbell的WeakEventHandler - 它确实无法通过设计使用匿名方法。当我意识到在99%的情况下,我需要这样的东西,他的原始解决方案会更安全,并且b)在少数情况下,我必须(注意:有到,而不是因为羊羔是如此漂亮和简洁),如果你有一点聪明,它可以使它工作。



您的例子似乎是一种简单的一个例子,其中有一点棘手可能会导致一个相当简洁的解决方案。

  
public static class Linker {
public static void Link(Publisher publisher,Control subscriber){
//匿名方法引用订阅者只有通过弱
//引用,所以它的存在不会干扰垃圾收集
var subscriber_weak_ref = new WeakReference(subscriber);

//只要匿名
//方法持有我们声明的引用并将其初始化为
//这个实例变量将保留在内存中//保留内存(还有,编译器抱怨关于未初始化的
//变量否则)
EventHandler< ValueEventArgs< bool >> handler = null;

//当处理程序创建时,它将获取对本地使用的
//变量的引用,在函数后保留它们在内存中
//范围结束
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 {
//取消订阅内部委托是有风险的,但
//因为只有一个实例存在,没有其他人有一个
//引用我们可以做这个
((发布者)发件人).EnabledChanged - = handler;

//通过将原始实例变量指针分配给null
//我们确保没有其他参考匿名
//方法,它可以被收集。之后,弱
//引用和处理程序指针本身也将符合
// collection的资格。
handler = null;
}
};

publisher.EnabledChanged + = handler;
}
}

WPF的弱事件模式传言是很多开销,所以在这种特殊的情况下,我不会使用它。此外,在WinForm应用程序中引用核心WPF库似乎有点沉重。


UPDATE

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

Original 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.

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: It's a winforms application, but we've upgraded to VS2008 and .Net 3.5, should I consider using the Weak Event pattern?)

(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...)

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.

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;
    }
}

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天全站免登陆