2 * Copyright (c) 2020 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
19 using System.Collections.Generic;
20 using System.Reflection;
24 internal class WeakEvent<T>
26 private List<WeakHandler<T>> handlers = new List<WeakHandler<T>>();
28 protected int Count => handlers.Count;
30 public virtual void Add(T handler)
32 handlers.Add(new WeakHandler<T>(handler));
35 public virtual void Remove(T handler)
37 handlers.RemoveAll(item => !item.IsAlive || item.Equals(handler));
40 public void Invoke(object sender, EventArgs args)
42 var disposed = new HashSet<WeakHandler<T>>();
44 foreach (var item in handlers)
48 item.Invoke(sender, args);
54 handlers.RemoveAll(disposed.Contains);
57 internal class WeakHandler<U>
59 private WeakReference weakReference;
60 private MethodInfo methodInfo;
62 public WeakHandler(U handler)
64 Delegate d = (Delegate)(object)handler;
65 if (d.Target != null) weakReference = new WeakReference(d.Target);
66 methodInfo = d.Method;
69 public bool Equals(U handler)
71 Delegate other = (Delegate)(object)handler;
72 return other != null && other.Target == weakReference?.Target && other.Method.Equals(methodInfo);
75 public bool IsAlive => weakReference == null || weakReference.IsAlive;
77 public void Invoke(params object[] args)
79 if (weakReference == null)
81 Delegate.CreateDelegate(typeof(U), methodInfo).DynamicInvoke(args);
85 // Because GC is done in other thread,
86 // it needs to check again that the reference is still alive before calling method.
87 // To do that, the reference should be assigned to the local variable first.
88 var localRefCopied = weakReference.Target;
90 // Do not change this to if (weakReference.Target != null)
91 if (localRefCopied != null) Delegate.CreateDelegate(typeof(U), localRefCopied, methodInfo).DynamicInvoke(args);
98 /// Internal class that helps to make a proxy weak event connecting to a normal source event.
99 /// Note that the source event will have a strong reference of the WeakEventProxy instance instead of handler's.
100 /// Please replace it to WeakEventManager after version up.
102 internal abstract class WeakEventProxy<EventArgsT> : WeakEvent<EventHandler<EventArgsT>>
104 protected abstract void ConnectToEvent(EventHandler<EventArgsT> handler);
106 protected abstract void DisconnectToEvent(EventHandler<EventArgsT> handler);
108 public override void Add(EventHandler<EventArgsT> handler)
112 ConnectToEvent(OnEventInvoked);
118 public override void Remove(EventHandler<EventArgsT> handler)
120 base.Remove(handler);
124 DisconnectToEvent(OnEventInvoked);
128 private void OnEventInvoked(object sender, EventArgsT args)
130 Invoke(sender, args as EventArgs);