Merge pull request #1949 from lewurm/fixtype
[mono.git] / mcs / class / corlib / System / MulticastDelegate.cs
1 //
2 // System.MultiCastDelegate.cs
3 //
4 // Authors:
5 //   Miguel de Icaza (miguel@ximian.com)
6 //   Daniel Stodden (stodden@in.tum.de)
7 //
8 // (C) Ximian, Inc.  http://www.ximian.com
9 //
10
11 //
12 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
13 //
14 // Permission is hereby granted, free of charge, to any person obtaining
15 // a copy of this software and associated documentation files (the
16 // "Software"), to deal in the Software without restriction, including
17 // without limitation the rights to use, copy, modify, merge, publish,
18 // distribute, sublicense, and/or sell copies of the Software, and to
19 // permit persons to whom the Software is furnished to do so, subject to
20 // the following conditions:
21 // 
22 // The above copyright notice and this permission notice shall be
23 // included in all copies or substantial portions of the Software.
24 // 
25 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
26 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
27 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
28 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
29 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
30 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
31 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
32 //
33
34 using System.Collections;
35 using System.Collections.Generic;
36 using System.Runtime.Serialization;
37 using System.Runtime.InteropServices;
38
39 namespace System
40 {
41         [System.Runtime.InteropServices.ComVisible (true)]
42         [Serializable]
43         [StructLayout (LayoutKind.Sequential)]
44         public abstract class MulticastDelegate : Delegate
45         {
46                 Delegate[] delegates;
47
48                 protected MulticastDelegate (object target, string method)
49                         : base (target, method)
50                 {
51                 }
52
53                 protected MulticastDelegate (Type target, string method)
54                         : base (target, method)
55                 {
56                 }
57                 
58                 public override void GetObjectData (SerializationInfo info, StreamingContext context)
59                 {
60                         base.GetObjectData  (info, context);
61                 }
62
63                 protected sealed override object DynamicInvokeImpl (object[] args)
64                 {
65                         if (delegates == null) {
66                                 return base.DynamicInvokeImpl (args);
67                         } else {
68                                 object r;
69                                 int i = 0, len = delegates.Length;
70                                 do {
71                                         r = delegates [i].DynamicInvoke (args);
72                                 } while (++i < len);
73                                 return r;
74                         }
75                 }
76
77                 // Some high-performance applications use this internal property
78                 // to avoid using a slow path to determine if there is more than one handler
79                 // This brings an API that we removed in f410e545e2db0e0dc338673a6b10a5cfd2d3340f
80                 // which some users depeneded on
81                 //
82                 // This is an example of code that used this:
83                 // https://gist.github.com/migueldeicaza/cd99938c2a4372e7e5d5
84                 //
85                 // Do not remove this API
86                 internal bool HasSingleTarget {
87                         get { return delegates == null; }
88                 }
89
90                 // <remarks>
91                 //   Equals: two multicast delegates are equal if their base is equal
92                 //   and their invocations list is equal.
93                 // </remarks>
94                 public sealed override bool Equals (object obj)
95                 {
96                         if (!base.Equals (obj))
97                                 return false;
98
99                         MulticastDelegate d = obj as MulticastDelegate;
100                         if (d == null)
101                                 return false;
102
103                         if (delegates == null && d.delegates == null) {
104                                 return true;
105                         } else if (delegates == null ^ d.delegates == null) {
106                                 return false;
107                         } else {
108                                 if (delegates.Length != d.delegates.Length)
109                                         return false;
110
111                                 for (int i = 0; i < delegates.Length; ++i) {
112                                         if (!delegates [i].Equals (d.delegates [i]))
113                                                 return false;
114                                 }
115
116                                 return true;
117                         }
118                 }
119
120                 //
121                 // FIXME: This could use some improvements.
122                 //
123                 public sealed override int GetHashCode ()
124                 {
125                         return base.GetHashCode ();
126                 }
127
128                 // <summary>
129                 //   Return, in order of invocation, the invocation list
130                 //   of a MulticastDelegate
131                 // </summary>
132                 public sealed override Delegate[] GetInvocationList ()
133                 {
134                         if (delegates != null)
135                                 return (Delegate[]) delegates.Clone ();
136                         else
137                                 return new Delegate[1] { this };
138                 }
139
140                 // <summary>
141                 //   Combines this MulticastDelegate with the (Multicast)Delegate `follow'.
142                 //   This does _not_ combine with Delegates. ECMA states the whole delegate
143                 //   thing should have better been a simple System.Delegate class.
144                 //   Compiler generated delegates are always MulticastDelegates.
145                 // </summary>
146                 protected sealed override Delegate CombineImpl (Delegate follow)
147                 {
148                         if (follow == null)
149                                 return this;
150
151                         MulticastDelegate other = (MulticastDelegate) follow;
152
153                         MulticastDelegate ret = AllocDelegateLike_internal (this);
154
155                         if (delegates == null && other.delegates == null) {
156                                 ret.delegates = new Delegate [2] { this, other };
157                         } else if (delegates == null) {
158                                 ret.delegates = new Delegate [1 + other.delegates.Length];
159
160                                 ret.delegates [0] = this;
161                                 Array.Copy (other.delegates, 0, ret.delegates, 1, other.delegates.Length);
162                         } else if (other.delegates == null) {
163                                 ret.delegates = new Delegate [delegates.Length + 1];
164
165                                 Array.Copy (delegates, 0, ret.delegates, 0, delegates.Length);
166                                 ret.delegates [ret.delegates.Length - 1] = other;
167                         } else {
168                                 ret.delegates = new Delegate [delegates.Length + other.delegates.Length];
169
170                                 Array.Copy (delegates, 0, ret.delegates, 0, delegates.Length);
171                                 Array.Copy (other.delegates, 0, ret.delegates, delegates.Length, other.delegates.Length);
172                         }
173
174                         return ret;
175                 }
176
177                 protected sealed override Delegate RemoveImpl (Delegate value)
178                 {
179                         if (value == null)
180                                 return this;
181
182                         MulticastDelegate other = (MulticastDelegate) value;
183
184                         if (delegates == null && other.delegates == null) {
185                                 /* if they are not equal and the current one is not
186                                  * a multicastdelegate then we cannot delete it */
187                                 return this.Equals (other) ? null : this;
188                         } else if (delegates == null) {
189                                 foreach (var d in other.delegates) {
190                                         if (this.Equals (d))
191                                                 return null;
192                                 }
193                                 return this;
194                         } else if (other.delegates == null) {
195                                 int idx = Array.LastIndexOf (delegates, other);
196                                 if (idx == -1)
197                                         return this;
198
199                                 if (delegates.Length <= 1) {
200                                         /* delegates.Length should never be equal or
201                                          * lower than 1, it should be 2 or greater */
202                                         throw new InvalidOperationException ();
203                                 }
204
205                                 if (delegates.Length == 2)
206                                         return delegates [idx == 0 ? 1 : 0];
207
208                                 MulticastDelegate ret = AllocDelegateLike_internal (this);
209                                 ret.delegates = new Delegate [delegates.Length - 1];
210
211                                 Array.Copy (delegates, ret.delegates, idx);
212                                 Array.Copy (delegates, idx + 1, ret.delegates, idx, delegates.Length - idx - 1);
213
214                                 return ret;
215                         } else {
216                                 /* wild case : remove MulticastDelegate from MulticastDelegate
217                                  * complexity is O(m * n), with n the number of elements in
218                                  * this.delegates and m the number of elements in other.delegates */
219                                 MulticastDelegate ret = AllocDelegateLike_internal (this);
220                                 ret.delegates = new Delegate [delegates.Length];
221
222                                 /* we should use a set with O(1) lookup complexity
223                                  * but HashSet is implemented in System.Core.dll */
224                                 List<Delegate> other_delegates = new List<Delegate> ();
225                                 for (int i = 0; i < other.delegates.Length; ++i)
226                                         other_delegates.Add (other.delegates [i]);
227
228                                 int idx = delegates.Length;
229
230                                 /* we need to remove elements from the end to the beginning, as
231                                  * the addition and removal of delegates behaves like a stack */
232                                 for (int i = delegates.Length - 1; i >= 0; --i) {
233                                         /* if delegates[i] is not in other_delegates,
234                                          * then we can safely add it to ret.delegates
235                                          * otherwise we remove it from other_delegates */
236                                         if (!other_delegates.Remove (delegates [i]))
237                                                 ret.delegates [--idx] = delegates [i];
238                                 }
239
240                                 /* the elements are at the end of the array, we
241                                  * need to move them back to the beginning of it */
242                                 int count = delegates.Length - idx;
243                                 Array.Copy (ret.delegates, idx, ret.delegates, 0, count);
244
245                                 if (count == 0)
246                                         return null;
247
248                                 if (count == 1)
249                                         return ret.delegates [0];
250
251                                 if (count != delegates.Length)
252                                         Array.Resize (ref ret.delegates, count);
253
254                                 return ret;
255                         }
256                 }
257
258                 public static bool operator == (MulticastDelegate d1, MulticastDelegate d2)
259                 {
260                         if (d1 == null)
261                                 return d2 == null;
262
263                         return d1.Equals (d2);
264                 }
265                 
266                 public static bool operator != (MulticastDelegate d1, MulticastDelegate d2)
267                 {
268                         if (d1 == null)
269                                 return d2 != null;
270
271                         return !d1.Equals (d2);
272                 }
273         }
274 }