a2745cf5500528241284a9cd5311542fdb716b99
[platform/core/csapi/tizenfx.git] / src / Tizen.NUI / src / internal / Common / WeakEvent.cs
1 /*
2  * Copyright (c) 2020 Samsung Electronics Co., Ltd.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  *
16  */
17
18 using System;
19 using System.Collections.Generic;
20 using System.Reflection;
21
22 namespace Tizen.NUI
23 {
24     internal class WeakEvent<T>
25     {
26         private List<WeakHandler<T>> handlers = new List<WeakHandler<T>>();
27
28         protected int Count => handlers.Count;
29
30         public virtual void Add(T handler)
31         {
32             handlers.Add(new WeakHandler<T>(handler));
33         }
34
35         public virtual void Remove(T handler)
36         {
37             handlers.RemoveAll(item => !item.IsAlive || item.Equals(handler));
38         }
39
40         public void Invoke(object sender, EventArgs args)
41         {
42             var disposed = new HashSet<WeakHandler<T>>();
43
44             foreach (var item in handlers)
45             {
46                 if (item.IsAlive)
47                 {
48                     item.Invoke(sender, args);
49                     continue;
50                 }
51                 disposed.Add(item);
52             }
53
54             handlers.RemoveAll(disposed.Contains);
55         }
56
57         internal class WeakHandler<U>
58         {
59             private WeakReference weakReference;
60             private MethodInfo methodInfo;
61
62             public WeakHandler(U handler)
63             {
64                 Delegate d = (Delegate)(object)handler;
65                 if (d.Target != null) weakReference = new WeakReference(d.Target);
66                 methodInfo = d.Method;
67             }
68
69             public bool Equals(U handler)
70             {
71                 Delegate other = (Delegate)(object)handler;
72                 return other != null && other.Target == weakReference?.Target && other.Method.Equals(methodInfo);
73             }
74
75             public bool IsAlive => weakReference == null || weakReference.IsAlive;
76
77             public void Invoke(params object[] args)
78             {
79                 if (weakReference == null)
80                 {
81                     Delegate.CreateDelegate(typeof(U), methodInfo).DynamicInvoke(args);
82                 }
83                 else
84                 {
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;
89
90                     // Do not change this to if (weakReference.Target != null)
91                     if (localRefCopied != null) Delegate.CreateDelegate(typeof(U), localRefCopied, methodInfo).DynamicInvoke(args);
92                 }
93             }
94         }
95     }
96
97     /// <summary>
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.
101     /// </summary>
102     internal abstract class WeakEventProxy<EventArgsT> : WeakEvent<EventHandler<EventArgsT>>
103     {
104         protected abstract void ConnectToEvent(EventHandler<EventArgsT> handler);
105
106         protected abstract void DisconnectToEvent(EventHandler<EventArgsT> handler);
107
108         public override void Add(EventHandler<EventArgsT> handler)
109         {
110             if (Count == 0)
111             {
112                 ConnectToEvent(OnEventInvoked);
113             }
114
115             base.Add(handler);
116         }
117
118         public override void Remove(EventHandler<EventArgsT> handler)
119         {
120             base.Remove(handler);
121
122             if (Count == 0)
123             {
124                 DisconnectToEvent(OnEventInvoked);
125             }
126         }
127
128         private void OnEventInvoked(object sender, EventArgsT args)
129         {
130             Invoke(sender, args as EventArgs);
131         }
132     }
133 }