2005-04-21 Gonzalo Paniagua Javier <gonzalo@ximian.com>
[mono.git] / mcs / class / corlib / System / Delegate.cs
1 //
2 // System.Delegate.cs
3 //
4 // Authors:
5 //   Miguel de Icaza (miguel@ximian.com)
6 //   Daniel Stodden (stodden@in.tum.de)
7 //   Dietmar Maurer (dietmar@ximian.com)
8 //
9 // (C) Ximian, Inc.  http://www.ximian.com
10 //
11
12 //
13 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
14 //
15 // Permission is hereby granted, free of charge, to any person obtaining
16 // a copy of this software and associated documentation files (the
17 // "Software"), to deal in the Software without restriction, including
18 // without limitation the rights to use, copy, modify, merge, publish,
19 // distribute, sublicense, and/or sell copies of the Software, and to
20 // permit persons to whom the Software is furnished to do so, subject to
21 // the following conditions:
22 // 
23 // The above copyright notice and this permission notice shall be
24 // included in all copies or substantial portions of the Software.
25 // 
26 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
27 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
28 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
29 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
30 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
31 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
32 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
33 //
34
35 using System.Reflection;
36 using System.Runtime.Serialization;
37 using System.Runtime.CompilerServices;
38 using System.Runtime.InteropServices;
39
40 namespace System
41 {
42 #if NET_1_1
43         [ClassInterface (ClassInterfaceType.AutoDual)]
44 #endif
45         public abstract class Delegate : ICloneable, ISerializable
46         {
47                 private Type target_type;
48                 private object m_target;
49                 private string method_name;
50                 private IntPtr method_ptr;
51                 private IntPtr delegate_trampoline;
52                 private MethodInfo method_info;
53
54                 protected Delegate (object target, string method)
55                 {
56                         if (target == null)
57                                 throw new ArgumentNullException ("target");
58
59                         if (method == null)
60                                 throw new ArgumentNullException ("method");
61
62                         this.target_type = null;
63                         this.method_ptr = IntPtr.Zero;
64                         this.m_target = target;
65                         this.method_name = method;
66                 }
67
68                 protected Delegate (Type target, string method)
69                 {
70                         if (target == null)
71                                 throw new ArgumentNullException ("target");
72
73                         if (method == null)
74                                 throw new ArgumentNullException ("method");
75
76                         this.target_type = target;
77                         this.method_ptr = IntPtr.Zero;
78                         this.m_target = null;
79                         this.method_name = method;
80                 }
81
82                 public MethodInfo Method {
83                         get {
84                                 return method_info;
85                         }
86                 }
87
88                 public object Target {
89                         get {
90                                 return m_target;
91                         }
92                 }
93
94                 //
95                 // Methods
96                 //
97
98                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
99                 internal static extern Delegate CreateDelegate_internal (Type type, object target, MethodInfo info);
100
101                 public static Delegate CreateDelegate (Type type, MethodInfo method)
102                 {
103                         if (type == null)
104                                 throw new ArgumentNullException ("type");
105
106                         if (method == null)
107                                 throw new ArgumentNullException ("method");
108
109                         if (!type.IsSubclassOf (typeof (MulticastDelegate)))
110                                 throw new ArgumentException ("type is not a subclass of Multicastdelegate");
111
112                         if (!method.IsStatic)
113                                 throw new ArgumentException ("The method should be static.", "method");
114
115                         ParameterInfo[] delargs = type.GetMethod ("Invoke").GetParameters ();
116                         ParameterInfo[] args = method.GetParameters ();
117
118                         if (args.Length != delargs.Length)
119                                 throw new ArgumentException ("method argument length mismatch");
120                         
121                         int length = delargs.Length;
122                         for (int i = 0; i < length; i++)
123                                 if (delargs [i].ParameterType != args [i].ParameterType)
124                                         throw new ArgumentException ("method arguments are incompatible");
125
126                         return CreateDelegate_internal (type, null, method);
127                 }
128
129 #if NET_2_0
130                 public
131 #else
132                 internal
133 #endif
134                 static Delegate CreateDelegate (Type type, object target, MethodInfo method)
135                 {
136                         if (type == null)
137                                 throw new ArgumentNullException ("type");
138
139                         if (method == null)
140                                 throw new ArgumentNullException ("method");
141
142                         if (!type.IsSubclassOf (typeof (MulticastDelegate)))
143                                 throw new ArgumentException ("type is not a subclass of Multicastdelegate");
144
145                         return CreateDelegate_internal (type, target, method);
146
147                 }
148                 
149                 public static Delegate CreateDelegate (Type type, object target, string method)
150                 {
151                         return CreateDelegate(type, target, method, false);
152                 }
153
154                 public static Delegate CreateDelegate (Type type, Type target, string method)
155                 {
156                         if (type == null)
157                                 throw new ArgumentNullException ("type");
158
159                         if (target == null)
160                                 throw new ArgumentNullException ("target");
161
162                         if (method == null)
163                                 throw new ArgumentNullException ("method");
164
165                         if (!type.IsSubclassOf (typeof (MulticastDelegate)))
166                                 throw new ArgumentException ("type is not subclass of MulticastDelegate.");
167
168                         ParameterInfo[] delargs = type.GetMethod ("Invoke").GetParameters ();
169                         Type[] delargtypes = new Type [delargs.Length];
170
171                         for (int i=0; i<delargs.Length; i++)
172                                 delargtypes [i] = delargs [i].ParameterType;
173
174                         /* 
175                          * FIXME: we should check the caller has reflection permission
176                          * or if it lives in the same assembly...
177                          */
178                         BindingFlags flags = BindingFlags.ExactBinding | BindingFlags.Public | BindingFlags.Static | BindingFlags.NonPublic;
179                         MethodInfo info = target.GetMethod (method, flags, null, delargtypes, new ParameterModifier [0]);
180
181                         if (info == null)
182                                 throw new ArgumentException ("Couldn't bind to method.");
183
184                         return CreateDelegate_internal (type, null, info);
185                 }
186
187                 public static Delegate CreateDelegate (Type type, object target, string method, bool ignoreCase)
188                 {
189                         if (type == null)
190                                 throw new ArgumentNullException ("type");
191
192                         if (target == null)
193                                 throw new ArgumentNullException ("target");
194
195                         if (method == null)
196                                 throw new ArgumentNullException ("method");
197
198                         if (!type.IsSubclassOf (typeof (MulticastDelegate)))
199                                 throw new ArgumentException ("type");
200
201                         ParameterInfo[] delargs = type.GetMethod ("Invoke").GetParameters ();
202                         Type[] delargtypes = new Type [delargs.Length];
203
204                         for (int i=0; i<delargs.Length; i++)
205                                 delargtypes [i] = delargs [i].ParameterType;
206
207                         /* 
208                          * FIXME: we should check the caller has reflection permission
209                          * or if it lives in the same assembly...
210                          */
211                         BindingFlags flags = BindingFlags.ExactBinding | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance;
212
213                         if (ignoreCase)
214                                 flags |= BindingFlags.IgnoreCase;
215
216                         MethodInfo info = target.GetType ().GetMethod (method, flags, null, delargtypes, new ParameterModifier [0]);
217
218                         if (info == null)
219                                 throw new ArgumentException ("Couldn't bind to method '" + method + "'.");
220
221                         return CreateDelegate_internal (type, target, info);
222                 }
223
224                 public object DynamicInvoke (object[] args)
225                 {
226                         return DynamicInvokeImpl (args);
227                 }
228
229                 protected virtual object DynamicInvokeImpl (object[] args)
230                 {
231                         if (Method == null) {
232                                 Type[] mtypes = new Type [args.Length];
233                                 for (int i = 0; i < args.Length; ++i) {
234                                         mtypes [i] = args [i].GetType ();
235                                 }
236                                 method_info = m_target.GetType ().GetMethod (method_name, mtypes);
237                         }
238                         return Method.Invoke (m_target, args);
239                 }
240
241                 public virtual object Clone ()
242                 {
243                         return MemberwiseClone ();
244                 }
245
246                 public override bool Equals (object obj)
247                 {
248                         if (obj == null)
249                                 return false;
250
251                         Delegate d = (Delegate) obj;
252                         // Do not compare method_ptr, since it can point to a trampoline
253                         if ((d.target_type == target_type) && (d.m_target == m_target) &&
254                                 (d.method_name == method_name) && (d.method_info == method_info))
255                                 return true;
256
257                         return false;
258                 }
259
260                 public override int GetHashCode ()
261                 {
262                         return (int)method_ptr;
263                 }
264
265                 protected virtual MethodInfo GetMethodImpl ()
266                 {
267                         return Method;
268                 }
269
270                 // This is from ISerializable
271                 public virtual void GetObjectData (SerializationInfo info, StreamingContext context)
272                 {
273                         DelegateSerializationHolder.GetDelegateData (this, info, context);
274                 }
275
276                 public virtual Delegate[] GetInvocationList()
277                 {
278                         return new Delegate[] { this };
279                 }
280
281                 /// <symmary>
282                 ///   Returns a new MulticastDelegate holding the
283                 ///   concatenated invocation lists of MulticastDelegates a and b
284                 /// </symmary>
285                 public static Delegate Combine (Delegate a, Delegate b)
286                 {
287                         if (a == null) {
288                                 if (b == null)
289                                         return null;
290                                 return b;
291                         } else 
292                                 if (b == null)
293                                         return a;
294
295                         if (a.GetType () != b.GetType ())
296                                 throw new ArgumentException (Locale.GetText ("Incompatible Delegate Types."));
297                         
298                         return a.CombineImpl (b);
299                 }
300
301                 /// <symmary>
302                 ///   Returns a new MulticastDelegate holding the
303                 ///   concatenated invocation lists of an Array of MulticastDelegates
304                 /// </symmary>
305                 public static Delegate Combine (Delegate[] delegates)
306                 {
307                         if (delegates == null)
308                                 return null;
309
310                         Delegate retval = null;
311
312                         foreach (Delegate next in delegates)
313                                 retval = Combine (retval, next);
314
315                         return retval;
316                 }
317
318                 protected virtual Delegate CombineImpl (Delegate d)
319                 {
320                         throw new MulticastNotSupportedException ("");
321                 }
322
323                 public static Delegate Remove (Delegate source, Delegate value) 
324                 {
325                         if (source == null)
326                                 return null;
327
328                         return source.RemoveImpl (value);
329                 }
330
331                 protected virtual Delegate RemoveImpl (Delegate d)
332                 {
333                         if (this.Equals (d))
334                                 return null;
335
336                         return this;
337                 }
338 #if NET_1_1
339                 public static Delegate RemoveAll (Delegate source, Delegate value)
340                 {
341                         Delegate tmp = source;
342                         while ((source = Delegate.Remove (source, value)) != tmp)
343                                 tmp = source;
344
345                         return tmp;
346                 }
347 #endif
348                 public static bool operator == (Delegate a, Delegate b)
349                 {
350                         if ((object)a == null) {
351                                 if ((object)b == null)
352                                         return true;
353                                 return false;
354                         } else if ((object) b == null)
355                                 return false;
356                         
357                         return a.Equals (b);
358                 }
359
360                 public static bool operator != (Delegate a, Delegate b)
361                 {
362                         return !(a == b);
363                 }
364         }
365 }