C# 使用匿名委托进行事件处理时的垃圾收集

C# 使用匿名委托进行事件处理时的垃圾收集,c#,event-handling,garbage-collection,C#,Event Handling,Garbage Collection,更新 我把这里的各种答案结合在一起,形成了一个“确定的”答案 原始问题 在我的代码中,我有一个事件发布者,它存在于应用程序的整个生命周期中(这里简化为基本要素): 事实证明,在代码中有一个地方,订阅者控件是在表单中动态创建、添加和删除的。鉴于我对垃圾收集等方面的深入了解(即直到昨天都没有),我从未想过要在我身后清理垃圾,因为在绝大多数情况下,订阅者也会在应用程序的生命周期中生存 我已经花了一段时间摆弄它,但它对匿名代理不起作用(反正对我来说也不行) 这个问题还有解决的办法吗?我真的很想避免在整个

更新

我把这里的各种答案结合在一起,形成了一个“确定的”答案

原始问题

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

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

我已经花了一段时间摆弄它,但它对匿名代理不起作用(反正对我来说也不行)

这个问题还有解决的办法吗?我真的很想避免在整个商店里复制粘贴锅炉铭牌代码

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

(PS:这是一个WiFrm应用程序,但我们已经升级到VS2008和.NET 3.5,我应该考虑使用?)/P> (PPS:很好,但如果有人能想出一个与最弱的Thandler等效的方法,避免我必须记住显式取消链接/处置,那就太酷了……)

编辑我必须承认,我通过“回收”相关控件解决了这个问题。然而,解决方法又回来困扰着我,因为我使用的“钥匙”显然不是唯一的(sob)。我刚刚发现了其他链接(尝试了这个-似乎有点太弱了-即使目标仍然存在,GC也会清除委托,下面的问题与此相同),(强制您修改publisher,并且实际上无法处理匿名委托)和(Dustin Campbell引用为“不完整”)

我突然意识到,我所寻找的可能在语义上是不可能的——闭包被设计成“即使在我离开后仍会挂起”


我已经找到了另一种解决方法,因此我将继续使用它,如果您保留对匿名委托的引用,然后在从窗体中删除控件时将其删除,这将允许对控件和匿名委托进行垃圾收集,则会挂起。

比如说:

public static class Linker
{

    //(Non-lambda version, I'm not comfortable with lambdas:)
    public static EventHandler<ValueEventArgs<bool>> Link(Publisher publisher, Control subscriber)
    {
         EventHandler<ValueEventArgs<bool>> handler = delegate(object sender, ValueEventArgs<bool> e)
             {
                  subscriber.Enabled = e.Value;
             };
         publisher.EnabledChanged += handler;
         return handler;
    }

    public static void UnLink(Publisher publisher, EventHandler<ValueEventArgs<bool>> handler)
    {
        publisher.EnabledChanged -= handler;
    }

}
公共静态类链接器
{
//(非lambda版本,我对lambda不满意:)
公共静态EventHandler链接(发布服务器、控制订阅服务器)
{
EventHandler=委托(对象发送方,ValueEventArgs e)
{
subscriber.Enabled=e.Value;
};
publisher.EnabledChanged+=处理程序;
返回处理程序;
}
公共静态无效取消链接(发布服务器、事件处理程序)
{
publisher.EnabledChanged-=处理程序;
}
}

有关删除代理的示例,请参见。

我最近根据WeakReference编写的一些示例代码:

// strongly typed weak reference
public class WeakReference<T> : WeakReference
    where T : class
{
    public WeakReference(T target)
        : base(target)
    { }

    public WeakReference(T target, bool trackResurrection)
        : base(target, trackResurrection)
    { }

    public new T Target
    {
        get { return base.Target as T; }
        set { base.Target = value; }
    }
}

// weak referenced generic event handler
public class WeakEventHandler<TEventArgs> : WeakReference<EventHandler<TEventArgs>>
    where TEventArgs : EventArgs
{
    public WeakEventHandler(EventHandler<TEventArgs> target)
        : base(target)
    { }

    protected void Invoke(object sender, TEventArgs e)
    {
        if (Target != null)
        {
            Target(sender, e);
        }
    }

    public static implicit operator EventHandler<TEventArgs>(WeakEventHandler<TEventArgs> weakEventHandler)
    {
        if (weakEventHandler != null)
        {
            if (weakEventHandler.IsAlive)
            {
                return weakEventHandler.Invoke;
            }
        }

        return null;
    }
}

// weak reference common event handler
public class WeakEventHandler : WeakReference<EventHandler>
{
    public WeakEventHandler(EventHandler target)
        : base(target)
    { }

    protected void Invoke(object sender, EventArgs e)
    {
        if (Target != null)
        {
            Target(sender, e);
        }
    }

    public static implicit operator EventHandler(WeakEventHandler weakEventHandler)
    {
        if (weakEventHandler != null)
        {
            if (weakEventHandler.IsAlive)
            {
                return weakEventHandler.Invoke;
            }
        }

        return null;
    }
}

// observable class, fires events
public class Observable
{
    public Observable() { Console.WriteLine("new Observable()"); }
    ~Observable() { Console.WriteLine("~Observable()"); }

    public event EventHandler OnChange;

    protected virtual void DoOnChange()
    {
        EventHandler handler = OnChange;

        if (handler != null)
        {
            Console.WriteLine("DoOnChange()");
            handler(this, EventArgs.Empty);
        }
    }

    public void Change()
    {
        DoOnChange();
    }
}

// observer, event listener
public class Observer
{
    public Observer() { Console.WriteLine("new Observer()"); }
    ~Observer() { Console.WriteLine("~Observer()"); }

    public void OnChange(object sender, EventArgs e)
    {
        Console.WriteLine("-> Observer.OnChange({0}, {1})", sender, e);
    }
}

// sample usage and test code
public static class Program
{
    static void Main()
    {
        Observable subject = new Observable();
        Observer watcher = new Observer();

        Console.WriteLine("subscribe new WeakEventHandler()\n");
        subject.OnChange += new WeakEventHandler(watcher.OnChange);
        subject.Change();

        Console.WriteLine("\nObserver = null, GC");
        watcher = null;
        GC.Collect(0, GCCollectionMode.Forced);
        GC.WaitForPendingFinalizers();

        subject.Change();

        if (Debugger.IsAttached)
        {
            Console.Write("Press any key to continue . . . ");
            Console.ReadKey(true);
        }
    }
}

(请注意,取消订阅(=)不起作用)

我知道这个问题很古老,但见鬼——我找到了,我想其他人也可能找到它。我正试图解决一个相关的问题,可能有一些见解

您提到Dustin Campbell的WeakEventHandler-它确实无法设计匿名方法。当我意识到a)在99%的情况下我需要这样的东西时,他的原始解决方案会更安全,b)在我必须的少数情况下(注意:必须,而不是“因为lambdas更漂亮、更简洁,所以想要”),如果你再聪明一点,它就有可能工作

您的示例似乎正是一种一次性的情况,在这种情况下,使用一些技巧可以得到一个相当简洁的解决方案


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

公共静态类链接器{
公共静态无效链接(发布服务器、控制订阅服务器){
//匿名方法仅通过弱引用订阅服务器
//引用,所以它的存在不会干扰垃圾收集
var subscriber\u弱\u ref=新的WeakReference(订户);
//该实例变量将保留在内存中,只要匿名
//方法持有我们声明并初始化它的引用
//保留内存(此外,编译器会抱怨未初始化
//变量(否则)
EventHandler=null;
//创建处理程序时,它将获取对本地
//函数中使用的变量,在函数之后将它们保存在内存中
//范围结束
handler=委托(对象发送方,ValueEventArgs e){
var subscriber_strong_ref=作为控制的subscriber_Slong_ref.目标;
if(订户\u强\u参考!=null)
订户\u强\u参考启用=e.值;
否则{
//从代理本身取消订阅是有风险的,但是
//因为只有一个实例存在,没有其他实例具有
//参考它,我们可以这样做
((发布者)发送者).EnabledChanged-=处理程序;
//通过将原始实例变量指针指定为null
//我们确保没有任何其他内容引用匿名者
//方法和它可以被收集。在此之后,弱
//引用和处理程序指针本身将有资格
//收藏也是如此。
handler=null;
}
};
publisher.EnabledChanged+=处理程序;
}
}
据说WPF弱事件模式会带来很多开销,所以在这种特殊情况下,我不会使用它。此外,在WinForm应用程序中引用核心WPF库似乎也有点沉重。

进一步分析答案,我想尝试构建一个版本,在这个版本中,我不必事先确定我想要哪个事件
public static class Linker
{

    //(Non-lambda version, I'm not comfortable with lambdas:)
    public static EventHandler<ValueEventArgs<bool>> Link(Publisher publisher, Control subscriber)
    {
         EventHandler<ValueEventArgs<bool>> handler = delegate(object sender, ValueEventArgs<bool> e)
             {
                  subscriber.Enabled = e.Value;
             };
         publisher.EnabledChanged += handler;
         return handler;
    }

    public static void UnLink(Publisher publisher, EventHandler<ValueEventArgs<bool>> handler)
    {
        publisher.EnabledChanged -= handler;
    }

}
// strongly typed weak reference
public class WeakReference<T> : WeakReference
    where T : class
{
    public WeakReference(T target)
        : base(target)
    { }

    public WeakReference(T target, bool trackResurrection)
        : base(target, trackResurrection)
    { }

    public new T Target
    {
        get { return base.Target as T; }
        set { base.Target = value; }
    }
}

// weak referenced generic event handler
public class WeakEventHandler<TEventArgs> : WeakReference<EventHandler<TEventArgs>>
    where TEventArgs : EventArgs
{
    public WeakEventHandler(EventHandler<TEventArgs> target)
        : base(target)
    { }

    protected void Invoke(object sender, TEventArgs e)
    {
        if (Target != null)
        {
            Target(sender, e);
        }
    }

    public static implicit operator EventHandler<TEventArgs>(WeakEventHandler<TEventArgs> weakEventHandler)
    {
        if (weakEventHandler != null)
        {
            if (weakEventHandler.IsAlive)
            {
                return weakEventHandler.Invoke;
            }
        }

        return null;
    }
}

// weak reference common event handler
public class WeakEventHandler : WeakReference<EventHandler>
{
    public WeakEventHandler(EventHandler target)
        : base(target)
    { }

    protected void Invoke(object sender, EventArgs e)
    {
        if (Target != null)
        {
            Target(sender, e);
        }
    }

    public static implicit operator EventHandler(WeakEventHandler weakEventHandler)
    {
        if (weakEventHandler != null)
        {
            if (weakEventHandler.IsAlive)
            {
                return weakEventHandler.Invoke;
            }
        }

        return null;
    }
}

// observable class, fires events
public class Observable
{
    public Observable() { Console.WriteLine("new Observable()"); }
    ~Observable() { Console.WriteLine("~Observable()"); }

    public event EventHandler OnChange;

    protected virtual void DoOnChange()
    {
        EventHandler handler = OnChange;

        if (handler != null)
        {
            Console.WriteLine("DoOnChange()");
            handler(this, EventArgs.Empty);
        }
    }

    public void Change()
    {
        DoOnChange();
    }
}

// observer, event listener
public class Observer
{
    public Observer() { Console.WriteLine("new Observer()"); }
    ~Observer() { Console.WriteLine("~Observer()"); }

    public void OnChange(object sender, EventArgs e)
    {
        Console.WriteLine("-> Observer.OnChange({0}, {1})", sender, e);
    }
}

// sample usage and test code
public static class Program
{
    static void Main()
    {
        Observable subject = new Observable();
        Observer watcher = new Observer();

        Console.WriteLine("subscribe new WeakEventHandler()\n");
        subject.OnChange += new WeakEventHandler(watcher.OnChange);
        subject.Change();

        Console.WriteLine("\nObserver = null, GC");
        watcher = null;
        GC.Collect(0, GCCollectionMode.Forced);
        GC.WaitForPendingFinalizers();

        subject.Change();

        if (Debugger.IsAttached)
        {
            Console.Write("Press any key to continue . . . ");
            Console.ReadKey(true);
        }
    }
}
new Observable()
new Observer()
subscribe new WeakEventHandler()

DoOnChange()
-> Observer.OnChange(ConsoleApplication4.Observable, System.EventArgs)

Observer = null, GC
~Observer()
DoOnChange()
~Observable()
Press any key to continue . . .

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;
    }
}
private static void SetAnyGenericHandler<S, T>(
     Action<EventHandler<T>> add,     //to add event listener to publisher
     Action<EventHandler<T>> remove,  //to remove event listener from publisher
     S subscriber,                    //ref to subscriber (to pass to consume)
     Action<S, T> consume)            //called when event is raised*
         where T : EventArgs 
         where S : class
{
    var subscriber_weak_ref = new WeakReference(subscriber);
    EventHandler<T> handler = null;
    handler = delegate(object sender, T e)
    {
        var subscriber_strong_ref = subscriber_weak_ref.Target as S;
        if(subscriber_strong_ref != null)
        {
            Console.WriteLine("New event received by subscriber");
            consume(subscriber_strong_ref, e);
        }
        else
        {
            remove(handler);
            handler = null;
        }
    };
    add(handler);
}
SetAnyGenericHandler(
    h => publisher.EnabledChanged += h, 
    h => publisher.EnabledChanged -= h, 
    subscriber, 
    (Subscriber s, ValueEventArgs<bool> e) => s.Enabled = e.Value);
SetAnyGenericHandler<Subscriber, ValueEventArgs<bool>>(
    h => publisher.EnabledChanged += h, 
    h => publisher.EnabledChanged -= h, 
    subscriber, 
    (s, e) => s.Enabled = e.Value);