2007-05-23 Atsushi Enomoto <atsushi@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 #if NET_2_0
46         [System.Runtime.InteropServices.ComVisible (true)]
47         [Serializable]
48 #endif
49         public abstract class Delegate : ICloneable, ISerializable
50         {
51                 private Type target_type;
52                 private object m_target;
53                 private string method_name;
54                 private IntPtr method_ptr;
55                 private IntPtr delegate_trampoline;
56                 private MethodInfo method_info;
57
58                 protected Delegate (object target, string method)
59                 {
60                         if (target == null)
61                                 throw new ArgumentNullException ("target");
62
63                         if (method == null)
64                                 throw new ArgumentNullException ("method");
65
66                         this.target_type = null;
67                         this.method_ptr = IntPtr.Zero;
68                         this.m_target = target;
69                         this.method_name = method;
70                 }
71
72                 protected Delegate (Type target, string method)
73                 {
74                         if (target == null)
75                                 throw new ArgumentNullException ("target");
76
77                         if (method == null)
78                                 throw new ArgumentNullException ("method");
79
80                         this.target_type = target;
81                         this.method_ptr = IntPtr.Zero;
82                         this.m_target = null;
83                         this.method_name = method;
84                 }
85
86                 public MethodInfo Method {
87                         get {
88                                 return method_info;
89                         }
90                 }
91
92                 public object Target {
93                         get {
94                                 return m_target;
95                         }
96                 }
97
98                 //
99                 // Methods
100                 //
101
102                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
103                 internal static extern Delegate CreateDelegate_internal (Type type, object target, MethodInfo info);
104
105 #if NET_2_0
106                 public
107 #else
108                 internal
109 #endif
110                 static Delegate CreateDelegate (Type type, MethodInfo method, bool throwOnBindFailure)
111                 {
112                         if (type == null)
113                                 throw new ArgumentNullException ("type");
114
115                         if (method == null)
116                                 throw new ArgumentNullException ("method");
117
118                         if (!type.IsSubclassOf (typeof (MulticastDelegate)))
119                                 throw new ArgumentException ("type is not a subclass of Multicastdelegate");
120
121                         if (!method.IsStatic)
122                                 if (throwOnBindFailure)
123                                         throw new ArgumentException ("The method should be static.", "method");
124                                 else
125                                         return null;
126
127                         MethodInfo invoke = type.GetMethod ("Invoke");
128
129                         // FIXME: Check the return type on the 1.0 profile as well
130 #if NET_2_0
131                         Type returnType = method.ReturnType;
132                         Type delReturnType = invoke.ReturnType;
133                         bool returnMatch = returnType == delReturnType;
134
135                         if (!returnMatch) {
136                                 // Delegate covariance
137                                 if (!delReturnType.IsValueType && (delReturnType != typeof (ValueType)) && (delReturnType.IsAssignableFrom (returnType)))
138                                         returnMatch = true;
139                         }
140
141                         if (!returnMatch)
142                                 if (throwOnBindFailure)
143                                         throw new ArgumentException ("method return type is incompatible");
144                                 else
145                                         return null;
146 #endif
147
148                         ParameterInfo[] delargs = invoke.GetParameters ();
149                         ParameterInfo[] args = method.GetParameters ();
150
151                         if (args.Length != delargs.Length)
152                                 if (throwOnBindFailure)
153                                         throw new ArgumentException ("method argument length mismatch");
154                                 else
155                                         return null;
156                         
157                         int length = delargs.Length;
158                         for (int i = 0; i < length; i++) {
159                                 bool match = delargs [i].ParameterType == args [i].ParameterType;
160
161 #if NET_2_0
162                                 // Delegate contravariance
163                                 if (!match) {
164                                         Type argType = delargs [i].ParameterType;
165
166                                         if (!argType.IsValueType && (argType != typeof (ValueType)) && (args [i].ParameterType.IsAssignableFrom (argType)))
167                                                 match = true;
168                                 }
169 #endif
170
171                                 if (!match)
172                                         if (throwOnBindFailure)
173                                                 throw new ArgumentException ("method arguments are incompatible");
174                                         else
175                                                 return null;
176                         }
177
178                         return CreateDelegate_internal (type, null, method);
179                 }
180
181                 public static Delegate CreateDelegate (Type type, MethodInfo method) {
182                         return CreateDelegate (type, method, true);
183                 }
184
185 #if NET_2_0
186                 public
187 #else
188                 internal
189 #endif
190                 static Delegate CreateDelegate (Type type, object target, MethodInfo method, bool throwOnBindFailure)
191                 {
192                         if (type == null)
193                                 throw new ArgumentNullException ("type");
194
195                         if (method == null)
196                                 throw new ArgumentNullException ("method");
197
198                         if (!type.IsSubclassOf (typeof (MulticastDelegate)))
199                                 throw new ArgumentException ("type is not a subclass of Multicastdelegate");
200
201                         return CreateDelegate_internal (type, target, method);
202
203                 }
204
205 #if NET_2_0
206                 public
207 #else
208                 internal
209 #endif
210                 static Delegate CreateDelegate (Type type, object target, MethodInfo method) {
211                         return CreateDelegate (type, target, method, true);
212                 }
213                 
214                 public static Delegate CreateDelegate (Type type, object target, string method)
215                 {
216                         return CreateDelegate(type, target, method, false);
217                 }
218
219                 public static Delegate CreateDelegate (Type type, Type target, string method)
220                 {
221                         if (type == null)
222                                 throw new ArgumentNullException ("type");
223
224                         if (target == null)
225                                 throw new ArgumentNullException ("target");
226
227                         if (method == null)
228                                 throw new ArgumentNullException ("method");
229
230                         if (!type.IsSubclassOf (typeof (MulticastDelegate)))
231                                 throw new ArgumentException ("type is not subclass of MulticastDelegate.");
232
233                         ParameterInfo[] delargs = type.GetMethod ("Invoke").GetParameters ();
234                         Type[] delargtypes = new Type [delargs.Length];
235
236                         for (int i=0; i<delargs.Length; i++)
237                                 delargtypes [i] = delargs [i].ParameterType;
238
239                         /* 
240                          * FIXME: we should check the caller has reflection permission
241                          * or if it lives in the same assembly...
242                          */
243                         BindingFlags flags = BindingFlags.ExactBinding | BindingFlags.Public | BindingFlags.Static | BindingFlags.NonPublic;
244                         MethodInfo info = target.GetMethod (method, flags, null, delargtypes, new ParameterModifier [0]);
245
246                         if (info == null)
247                                 throw new ArgumentException ("Couldn't bind to method.");
248
249                         return CreateDelegate_internal (type, null, info);
250                 }
251
252 #if NET_2_0
253                 public
254 #else
255                 internal
256 #endif
257                 static Delegate CreateDelegate (Type type, object target, string method, bool ignoreCase, bool throwOnBindFailure)
258                 {
259                         if (type == null)
260                                 throw new ArgumentNullException ("type");
261
262                         if (target == null)
263                                 throw new ArgumentNullException ("target");
264
265                         if (method == null)
266                                 throw new ArgumentNullException ("method");
267
268                         if (!type.IsSubclassOf (typeof (MulticastDelegate)))
269                                 throw new ArgumentException ("type");
270
271                         ParameterInfo[] delargs = type.GetMethod ("Invoke").GetParameters ();
272                         Type[] delargtypes = new Type [delargs.Length];
273
274                         for (int i=0; i<delargs.Length; i++)
275                                 delargtypes [i] = delargs [i].ParameterType;
276
277                         /* 
278                          * FIXME: we should check the caller has reflection permission
279                          * or if it lives in the same assembly...
280                          */
281                         BindingFlags flags = BindingFlags.ExactBinding | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance;
282
283                         if (ignoreCase)
284                                 flags |= BindingFlags.IgnoreCase;
285
286                         MethodInfo info = target.GetType ().GetMethod (method, flags, null, delargtypes, new ParameterModifier [0]);
287
288                         if (info == null)
289                                 if (throwOnBindFailure)
290                                         throw new ArgumentException ("Couldn't bind to method '" + method + "'.");
291                                 else
292                                         return null;
293
294                         return CreateDelegate_internal (type, target, info);
295                 }
296
297                 public static Delegate CreateDelegate (Type type, object target, string method, bool ignoreCase) {
298                         return CreateDelegate (type, target, method, ignoreCase, true);
299                 }
300
301 #if NET_2_0
302                 public object DynamicInvoke (params object[] args)
303 #else
304                 public object DynamicInvoke (object[] args)
305 #endif
306                 {
307                         return DynamicInvokeImpl (args);
308                 }
309
310                 protected virtual object DynamicInvokeImpl (object[] args)
311                 {
312                         if (Method == null) {
313                                 Type[] mtypes = new Type [args.Length];
314                                 for (int i = 0; i < args.Length; ++i) {
315                                         mtypes [i] = args [i].GetType ();
316                                 }
317                                 method_info = m_target.GetType ().GetMethod (method_name, mtypes);
318                         }
319
320 #if NET_2_0
321                         if ((m_target != null) && Method.IsStatic) {
322                                 // The delegate is bound to m_target
323                                 if (args != null) {
324                                         object[] newArgs = new object [args.Length + 1];
325                                         args.CopyTo (newArgs, 1);
326                                         newArgs [0] = m_target;
327                                         args = newArgs;
328                                 } else {
329                                         args = new object [] { m_target };
330                                 }
331                                 return Method.Invoke (null, args);
332                         }
333 #endif
334
335                         return Method.Invoke (m_target, args);
336                 }
337
338                 public virtual object Clone ()
339                 {
340                         return MemberwiseClone ();
341                 }
342
343                 public override bool Equals (object obj)
344                 {
345                         Delegate d = obj as Delegate;
346                         
347                         if (d == null)
348                                 return false;
349                         
350                         // Do not compare method_ptr, since it can point to a trampoline
351                         if ((d.target_type == target_type) && (d.m_target == m_target) &&
352                                 (d.method_name == method_name) && (d.method_info == method_info))
353                                 return true;
354
355                         return false;
356                 }
357
358                 public override int GetHashCode ()
359                 {
360                         // FIXME: Sync with Equals above
361                         return (int)method_ptr;
362                 }
363
364                 protected virtual MethodInfo GetMethodImpl ()
365                 {
366                         return Method;
367                 }
368
369                 // This is from ISerializable
370                 public virtual void GetObjectData (SerializationInfo info, StreamingContext context)
371                 {
372                         DelegateSerializationHolder.GetDelegateData (this, info, context);
373                 }
374
375                 public virtual Delegate[] GetInvocationList()
376                 {
377                         return new Delegate[] { this };
378                 }
379
380                 /// <symmary>
381                 ///   Returns a new MulticastDelegate holding the
382                 ///   concatenated invocation lists of MulticastDelegates a and b
383                 /// </symmary>
384                 public static Delegate Combine (Delegate a, Delegate b)
385                 {
386                         if (a == null) {
387                                 if (b == null)
388                                         return null;
389                                 return b;
390                         } else 
391                                 if (b == null)
392                                         return a;
393
394                         if (a.GetType () != b.GetType ())
395                                 throw new ArgumentException (Locale.GetText ("Incompatible Delegate Types."));
396                         
397                         return a.CombineImpl (b);
398                 }
399
400                 /// <symmary>
401                 ///   Returns a new MulticastDelegate holding the
402                 ///   concatenated invocation lists of an Array of MulticastDelegates
403                 /// </symmary>
404 #if NET_2_0
405                 [System.Runtime.InteropServices.ComVisible (true)]
406                 public static Delegate Combine (params Delegate[] delegates)
407 #else
408                 public static Delegate Combine (Delegate[] delegates)
409 #endif
410                 {
411                         if (delegates == null)
412                                 return null;
413
414                         Delegate retval = null;
415
416                         foreach (Delegate next in delegates)
417                                 retval = Combine (retval, next);
418
419                         return retval;
420                 }
421
422                 protected virtual Delegate CombineImpl (Delegate d)
423                 {
424                         throw new MulticastNotSupportedException ("");
425                 }
426
427                 public static Delegate Remove (Delegate source, Delegate value) 
428                 {
429                         if (source == null)
430                                 return null;
431
432                         return source.RemoveImpl (value);
433                 }
434
435                 protected virtual Delegate RemoveImpl (Delegate d)
436                 {
437                         if (this.Equals (d))
438                                 return null;
439
440                         return this;
441                 }
442 #if NET_1_1
443                 public static Delegate RemoveAll (Delegate source, Delegate value)
444                 {
445                         Delegate tmp = source;
446                         while ((source = Delegate.Remove (source, value)) != tmp)
447                                 tmp = source;
448
449                         return tmp;
450                 }
451 #endif
452                 public static bool operator == (Delegate a, Delegate b)
453                 {
454                         if ((object)a == null) {
455                                 if ((object)b == null)
456                                         return true;
457                                 return false;
458                         } else if ((object) b == null)
459                                 return false;
460                         
461                         return a.Equals (b);
462                 }
463
464                 public static bool operator != (Delegate a, Delegate b)
465                 {
466                         return !(a == b);
467                 }
468         }
469 }