2016-08-03 46 views
0

为什么在下面的例子中垃圾收集事件处理程序?
我希望垃圾收集后收到事件,但事实并非如此。
问题不是关于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; 
    } 

输出在控制台:
呼叫触发
EventReceived
调用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"); 
    } 
} 
+0

这就是“弱”的意思。当你[使用调试器]时,你的第二个片段不够脆弱(http://stackoverflow.com/questions/17130382/understanding-garbage-collection-in-net/17131389#17131389)。通过在用户计算机上运行代码的方式让它具有相同的行为方式。切换到发布版本并使用工具>选项>调试>常规>取消“抑制JIT优化”。 –

回答

0

第一个是预期的结果:如果引用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"); 
    } 
} 

请注意,您需要在发布模式下进行编译。在调试模式下,对象保持活动状态,直到方法结束(即使它们不再被引用),以使调试更容易。

+0

我错过了ec.EventReceived就像是指向函数的指针,而我只有ec.EventReceived的弱引用。该函数仍然存在(我有强烈的参考ec),但指针是垃圾收集。我对吗? –