Warum ist der Garbage-Collected-Event-Handler im folgenden Beispiel?
Ich würde erwarten, dass Ereignis nach Garbage Collection empfangen wird, aber es ist nicht.
Frage geht nicht über WeakEventManager.Warum wird dieser schwache Verweis auf Ereignishandlerabfälle erfasst?
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;
}
Ausgabe in der Konsole ist:
Aufruf Trigger
EventReceived
Aufruf System.GC.Collect
Trigger aufrufen
-> hier würde ich EventReceived erwarten
Im folgenden einfachen Beispiel Referenz Es wird kein Müll gesammelt und funktioniert wie erwartet.
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");
}
}
Das ist was "schwach" bedeutet. Ihr zweites Snippet ist nicht ziemlich schwach genug, wenn Sie [einen Debugger verwenden] (http://stackoverflow.com/questions/17130382/understanding-garbage-collection-in-net/17131389#17131389). Führen Sie den Code so aus, wie er auf dem Computer Ihres Benutzers ausgeführt wird. Wechseln Sie zum Release-Build und wählen Sie Extras> Optionen> Debugging> Allgemein> deaktivieren Sie "JIT-Optimierung unterdrücken". –