为什么垃圾收集事件处理程序在以下示例中? 我希望垃圾收集后收到事件,但事实并非如此 问题与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
呼叫触发器
- &GT;在这里我期待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 :(得分: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");
}
}
请注意,您需要在发布模式下进行编译。在调试模式下,对象保持活动直到方法结束(即使它们不再被引用),以使调试更容易。