2 // System.MultiCastDelegate.cs
5 // Miguel de Icaza (miguel@ximian.com)
6 // Daniel Stodden (stodden@in.tum.de)
8 // (C) Ximian, Inc. http://www.ximian.com
12 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
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:
22 // The above copyright notice and this permission notice shall be
23 // included in all copies or substantial portions of the Software.
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.
34 using System.Collections;
35 using System.Collections.Generic;
36 using System.Reflection;
37 using System.Runtime.Serialization;
38 using System.Runtime.InteropServices;
42 [System.Runtime.InteropServices.ComVisible (true)]
44 [StructLayout (LayoutKind.Sequential)]
45 public abstract class MulticastDelegate : Delegate
49 protected MulticastDelegate (object target, string method)
50 : base (target, method)
54 protected MulticastDelegate (Type target, string method)
55 : base (target, method)
59 public override void GetObjectData (SerializationInfo info, StreamingContext context)
61 base.GetObjectData (info, context);
64 protected sealed override object DynamicInvokeImpl (object[] args)
66 if (delegates == null) {
67 return base.DynamicInvokeImpl (args);
70 int i = 0, len = delegates.Length;
72 r = delegates [i].DynamicInvoke (args);
78 // Some high-performance applications use this internal property
79 // to avoid using a slow path to determine if there is more than one handler
80 // This brings an API that we removed in f410e545e2db0e0dc338673a6b10a5cfd2d3340f
81 // which some users depeneded on
83 // This is an example of code that used this:
84 // https://gist.github.com/migueldeicaza/cd99938c2a4372e7e5d5
86 // Do not remove this API
87 internal bool HasSingleTarget {
88 get { return delegates == null; }
92 // Equals: two multicast delegates are equal if their base is equal
93 // and their invocations list is equal.
95 public sealed override bool Equals (object obj)
97 if (!base.Equals (obj))
100 MulticastDelegate d = obj as MulticastDelegate;
104 if (delegates == null && d.delegates == null) {
106 } else if (delegates == null ^ d.delegates == null) {
109 if (delegates.Length != d.delegates.Length)
112 for (int i = 0; i < delegates.Length; ++i) {
113 if (!delegates [i].Equals (d.delegates [i]))
122 // FIXME: This could use some improvements.
124 public sealed override int GetHashCode ()
126 return base.GetHashCode ();
129 protected override MethodInfo GetMethodImpl ()
131 if (delegates != null)
132 return delegates [delegates.Length - 1].Method;
134 return base.GetMethodImpl ();
138 // Return, in order of invocation, the invocation list
139 // of a MulticastDelegate
141 public sealed override Delegate[] GetInvocationList ()
143 if (delegates != null)
144 return (Delegate[]) delegates.Clone ();
146 return new Delegate[1] { this };
150 // Combines this MulticastDelegate with the (Multicast)Delegate `follow'.
151 // This does _not_ combine with Delegates. ECMA states the whole delegate
152 // thing should have better been a simple System.Delegate class.
153 // Compiler generated delegates are always MulticastDelegates.
155 protected sealed override Delegate CombineImpl (Delegate follow)
160 MulticastDelegate other = (MulticastDelegate) follow;
162 MulticastDelegate ret = AllocDelegateLike_internal (this);
164 if (delegates == null && other.delegates == null) {
165 ret.delegates = new Delegate [2] { this, other };
166 } else if (delegates == null) {
167 ret.delegates = new Delegate [1 + other.delegates.Length];
169 ret.delegates [0] = this;
170 Array.Copy (other.delegates, 0, ret.delegates, 1, other.delegates.Length);
171 } else if (other.delegates == null) {
172 ret.delegates = new Delegate [delegates.Length + 1];
174 Array.Copy (delegates, 0, ret.delegates, 0, delegates.Length);
175 ret.delegates [ret.delegates.Length - 1] = other;
177 ret.delegates = new Delegate [delegates.Length + other.delegates.Length];
179 Array.Copy (delegates, 0, ret.delegates, 0, delegates.Length);
180 Array.Copy (other.delegates, 0, ret.delegates, delegates.Length, other.delegates.Length);
186 /* Based on the Boyer–Moore string search algorithm */
187 int LastIndexOf (Delegate[] haystack, Delegate[] needle)
189 if (haystack.Length < needle.Length)
192 if (haystack.Length == needle.Length) {
193 for (int i = 0; i < haystack.Length; ++i)
194 if (!haystack [i].Equals (needle [i]))
200 for (int i = haystack.Length - needle.Length, j; i >= 0;) {
201 for (j = 0; needle [j].Equals (haystack [i]); ++i, ++j) {
202 if (j == needle.Length - 1)
212 protected sealed override Delegate RemoveImpl (Delegate value)
217 MulticastDelegate other = (MulticastDelegate) value;
219 if (delegates == null && other.delegates == null) {
220 /* if they are not equal and the current one is not
221 * a multicastdelegate then we cannot delete it */
222 return this.Equals (other) ? null : this;
223 } else if (delegates == null) {
224 foreach (var d in other.delegates) {
229 } else if (other.delegates == null) {
230 int idx = Array.LastIndexOf (delegates, other);
234 if (delegates.Length <= 1) {
235 /* delegates.Length should never be equal or
236 * lower than 1, it should be 2 or greater */
237 throw new InvalidOperationException ();
240 if (delegates.Length == 2)
241 return delegates [idx == 0 ? 1 : 0];
243 MulticastDelegate ret = AllocDelegateLike_internal (this);
244 ret.delegates = new Delegate [delegates.Length - 1];
246 Array.Copy (delegates, ret.delegates, idx);
247 Array.Copy (delegates, idx + 1, ret.delegates, idx, delegates.Length - idx - 1);
251 /* wild case : remove MulticastDelegate from MulticastDelegate
252 * complexity is O(m + n), with n the number of elements in
253 * this.delegates and m the number of elements in other.delegates */
255 if (delegates.Equals (other.delegates))
258 /* we need to remove elements from the end to the beginning, as
259 * the addition and removal of delegates behaves like a stack */
260 int idx = LastIndexOf (delegates, other.delegates);
264 MulticastDelegate ret = AllocDelegateLike_internal (this);
265 ret.delegates = new Delegate [delegates.Length - other.delegates.Length];
267 Array.Copy (delegates, ret.delegates, idx);
268 Array.Copy (delegates, idx + other.delegates.Length, ret.delegates, idx, delegates.Length - idx - other.delegates.Length);
274 public static bool operator == (MulticastDelegate d1, MulticastDelegate d2)
279 return d1.Equals (d2);
282 public static bool operator != (MulticastDelegate d1, MulticastDelegate d2)
287 return !d1.Equals (d2);