簡體   English   中英

為什么會收集對事件處理程序垃圾的這種弱引用?

[英]Why is this weak reference to event handler garbage collected?

在以下示例中,為什么要垃圾收集事件處理程序?
我希望垃圾收集后收到事件,但事實並非如此。
問題不關乎WeakEventManager。

class WeakEventTest
{
    public static void Run() {
        EventConsumer ec = new EventConsumer();
        WeakEvent<EventArgs> weakEvent = new WeakEvent<EventArgs>();
        EventHandler<EventArgs> eh = ec.HandleEvent;
        weakEvent += new WeakReference<EventHandler<EventArgs>>(ec.HandleEvent);

        Console.WriteLine("Calling trigger");
        weakEvent.Trigger(null, EventArgs.Empty);

        Console.WriteLine("Calling System.GC.Collect");
        GC.Collect();
        GC.WaitForPendingFinalizers();
        GC.WaitForFullGCComplete();
        GC.Collect();

        // event handler not called here
        Console.WriteLine("Calling trigger");
        weakEvent.Trigger(null, EventArgs.Empty);
    }


}

class EventConsumer
{
    public void HandleEvent(object obj, EventArgs args)
    {
        Console.WriteLine("EventReceived");
    }
}

public class WeakEvent<T>
{
    private List<WeakReference<EventHandler<T>>> referenceList = new List<WeakReference<EventHandler<T>>>();
    private EventHandler<T> handler = null;

    public static WeakEvent<T> operator +(WeakEvent<T> a, EventHandler<T> b)
    {
        lock (a.referenceList)
        {
            a.handler += b;
        }
        return a;
    }

    public static WeakEvent<T> operator +(WeakEvent<T> a, WeakReference<EventHandler<T>> b)
    {
        lock (a.referenceList)
        {
            a.referenceList.Add(b);
        }
        return a;
    }

    public static WeakEvent<T> operator -(WeakEvent<T> a, EventHandler<T> b)
    {
        lock (a.referenceList)
        {
            for (int i = a.referenceList.Count - 1; i >= 0; i--)
            {
                WeakReference<EventHandler<T>> wr = a.referenceList[i];
                EventHandler<T> target;
                if (!wr.TryGetTarget(out target))
                {
                    a.referenceList.RemoveAt(i);
                    continue;
                }
                if (Object.ReferenceEquals(target, b))
                {
                    a.referenceList.RemoveAt(i);
                    break;
                }
            }
            a.handler -= b;
        }
        return a;
    }

    public void Trigger(object obj, T args)
    {
        lock (referenceList)
        {
            for (int i = referenceList.Count - 1; i >= 0; i--)
            {
                WeakReference<EventHandler<T>> wr = referenceList[i];
                EventHandler<T> target;
                if (!wr.TryGetTarget(out target))
                {
                    referenceList.RemoveAt(i);
                    continue;
                }
                target(obj, args);
            }

            if (handler != null)
            {
                handler(obj, args);
            }
        }
    }

    public WeakEvent<T> AddWeakHandler(EventHandler<T> b)
    {
        lock (referenceList)
        {
            referenceList.Add(new WeakReference<EventHandler<T>>(b));
        }
        return this;
    }

控制台中的輸出為:
呼叫觸發
收到事件
調用System.GC.Collect
呼叫觸發
->在這里我希望EventReceived

在下面的簡單示例中,引用不是垃圾回收,而是按預期工作。

class Program
{
    static void Main(string[] args)
    {
        var ec = new EventConsumer();
        var wr = new WeakReference<EventHandler<EventArgs>>(ec.EventReceived);

        EventHandler<EventArgs> target;
        if (wr.TryGetTarget(out target))
        {
            Console.WriteLine("Raising event");
            target(null, EventArgs.Empty);
        }

        Console.WriteLine("Calling System.GC.Collect");
        GC.Collect();
        GC.WaitForPendingFinalizers();
        GC.WaitForFullGCComplete();
        GC.Collect();

        EventHandler<EventArgs> target2;
        if (wr.TryGetTarget(out target2))
        {
            Console.WriteLine("Raising event");
            target2(null, EventArgs.Empty);
        }

        Console.ReadKey();
    }
}

public class EventConsumer
{
    public void EventReceived(object obj, EventArgs args)
    {
        Console.WriteLine("EventReceived");
    }
}

第一個是預期的結果:您僅使用弱引用來引用ec ,因此沒有理由不對其進行收集。

第二個示例更加微妙: ec保持活動狀態,因為您在target上保留了一個引用(反過來又引用了ec )。 只需清除該引用,您將觀察到與第一個示例相同的行為:

class Program
{
    static void Main(string[] args)
    {
        var ec = new EventConsumer();
        var wr = new WeakReference<EventHandler<EventArgs>>(ec.EventReceived);

        EventHandler<EventArgs> target;
        if (wr.TryGetTarget(out target))
        {
            Console.WriteLine("Raising event");
            target(null, EventArgs.Empty);
        }

        // Clear the reference
        target = null;

        Console.WriteLine("Calling System.GC.Collect");
        GC.Collect();
        GC.WaitForPendingFinalizers();
        GC.WaitForFullGCComplete();
        GC.Collect();

        EventHandler<EventArgs> target2;
        if (wr.TryGetTarget(out target2))
        {
            Console.WriteLine("Raising event");
            target2(null, EventArgs.Empty);
        }

        Console.ReadKey();
    }
}

public class EventConsumer
{
    public void EventReceived(object obj, EventArgs args)
    {
        Console.WriteLine("EventReceived");
    }
}

請注意,您需要在發布模式下進行編譯。 在調試模式下,對象將保持活動狀態直到方法結束(即使不再引用它們)也使調試更加容易。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM