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.Runtime.Serialization;
37 using System.Runtime.InteropServices;
41 [System.Runtime.InteropServices.ComVisible (true)]
43 [StructLayout (LayoutKind.Sequential)]
44 public abstract class MulticastDelegate : Delegate
48 protected MulticastDelegate (object target, string method)
49 : base (target, method)
53 protected MulticastDelegate (Type target, string method)
54 : base (target, method)
58 public override void GetObjectData (SerializationInfo info, StreamingContext context)
60 base.GetObjectData (info, context);
63 protected sealed override object DynamicInvokeImpl (object[] args)
65 if (delegates == null) {
66 return base.DynamicInvokeImpl (args);
69 int i = 0, len = delegates.Length;
71 r = delegates [i].DynamicInvoke (args);
78 // Equals: two multicast delegates are equal if their base is equal
79 // and their invocations list is equal.
81 public sealed override bool Equals (object obj)
83 if (!base.Equals (obj))
86 MulticastDelegate d = obj as MulticastDelegate;
90 if (delegates == null && d.delegates == null) {
92 } else if (delegates == null ^ d.delegates == null) {
95 if (delegates.Length != d.delegates.Length)
98 for (int i = 0; i < delegates.Length; ++i) {
99 if (!delegates [i].Equals (d.delegates [i]))
108 // FIXME: This could use some improvements.
110 public sealed override int GetHashCode ()
112 return base.GetHashCode ();
116 // Return, in order of invocation, the invocation list
117 // of a MulticastDelegate
119 public sealed override Delegate[] GetInvocationList ()
121 if (delegates != null)
122 return (Delegate[]) delegates.Clone ();
124 return new Delegate[1] { this };
128 // Combines this MulticastDelegate with the (Multicast)Delegate `follow'.
129 // This does _not_ combine with Delegates. ECMA states the whole delegate
130 // thing should have better been a simple System.Delegate class.
131 // Compiler generated delegates are always MulticastDelegates.
133 protected sealed override Delegate CombineImpl (Delegate follow)
138 MulticastDelegate other = (MulticastDelegate) follow;
140 MulticastDelegate ret = AllocDelegateLike_internal (this);
142 if (delegates == null && other.delegates == null) {
143 ret.delegates = new Delegate [2] { this, other };
144 } else if (delegates == null) {
145 ret.delegates = new Delegate [1 + other.delegates.Length];
147 ret.delegates [0] = this;
148 Array.Copy (other.delegates, 0, ret.delegates, 1, other.delegates.Length);
149 } else if (other.delegates == null) {
150 ret.delegates = new Delegate [delegates.Length + 1];
152 Array.Copy (delegates, 0, ret.delegates, 0, delegates.Length);
153 ret.delegates [ret.delegates.Length - 1] = other;
155 ret.delegates = new Delegate [delegates.Length + other.delegates.Length];
157 Array.Copy (delegates, 0, ret.delegates, 0, delegates.Length);
158 Array.Copy (other.delegates, 0, ret.delegates, delegates.Length, other.delegates.Length);
164 protected sealed override Delegate RemoveImpl (Delegate value)
169 MulticastDelegate other = (MulticastDelegate) value;
171 if (delegates == null && other.delegates == null) {
172 /* if they are not equal and the current one is not
173 * a multicastdelegate then we cannot delete it */
174 return this.Equals (other) ? null : this;
175 } else if (delegates == null) {
176 foreach (var d in other.delegates) {
181 } else if (other.delegates == null) {
182 int idx = Array.LastIndexOf (delegates, other);
186 if (delegates.Length <= 1) {
187 /* delegates.Length should never be equal or
188 * lower than 1, it should be 2 or greater */
189 throw new InvalidOperationException ();
192 if (delegates.Length == 2)
193 return delegates [idx == 0 ? 1 : 0];
195 MulticastDelegate ret = AllocDelegateLike_internal (this);
196 ret.delegates = new Delegate [delegates.Length - 1];
198 Array.Copy (delegates, ret.delegates, idx);
199 Array.Copy (delegates, idx + 1, ret.delegates, idx, delegates.Length - idx - 1);
203 /* wild case : remove MulticastDelegate from MulticastDelegate
204 * complexity is O(m * n), with n the number of elements in
205 * this.delegates and m the number of elements in other.delegates */
206 MulticastDelegate ret = AllocDelegateLike_internal (this);
207 ret.delegates = new Delegate [delegates.Length];
209 /* we should use a set with O(1) lookup complexity
210 * but HashSet is implemented in System.Core.dll */
211 List<Delegate> other_delegates = new List<Delegate> ();
212 for (int i = 0; i < other.delegates.Length; ++i)
213 other_delegates.Add (other.delegates [i]);
215 int idx = delegates.Length;
217 /* we need to remove elements from the end to the beginning, as
218 * the addition and removal of delegates behaves like a stack */
219 for (int i = delegates.Length - 1; i >= 0; --i) {
220 /* if delegates[i] is not in other_delegates,
221 * then we can safely add it to ret.delegates
222 * otherwise we remove it from other_delegates */
223 if (!other_delegates.Remove (delegates [i]))
224 ret.delegates [--idx] = delegates [i];
227 /* the elements are at the end of the array, we
228 * need to move them back to the beginning of it */
229 int count = delegates.Length - idx;
230 Array.Copy (ret.delegates, idx, ret.delegates, 0, count);
236 return ret.delegates [0];
238 if (count != delegates.Length)
239 Array.Resize (ref ret.delegates, count);
245 public static bool operator == (MulticastDelegate d1, MulticastDelegate d2)
250 return d1.Equals (d2);
253 public static bool operator != (MulticastDelegate d1, MulticastDelegate d2)
258 return !d1.Equals (d2);