5 // Miguel de Icaza (miguel@ximian.com)
6 // Daniel Stodden (stodden@in.tum.de)
7 // Dietmar Maurer (dietmar@ximian.com)
9 // (C) Ximian, Inc. http://www.ximian.com
13 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
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:
23 // The above copyright notice and this permission notice shall be
24 // included in all copies or substantial portions of the Software.
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.
35 using System.Reflection;
36 using System.Runtime.Serialization;
37 using System.Runtime.CompilerServices;
38 using System.Runtime.InteropServices;
43 [ClassInterface (ClassInterfaceType.AutoDual)]
46 [System.Runtime.InteropServices.ComVisible (true)]
49 public abstract class Delegate : ICloneable, ISerializable
51 #region Sync with object-internals.h
52 private IntPtr method_ptr;
53 private IntPtr invoke_impl;
54 private object m_target;
55 private IntPtr method;
56 private Type target_type;
57 private string method_name;
58 private IntPtr delegate_trampoline;
59 private MethodInfo method_info;
61 // Keep a ref of the MethodInfo passed to CreateDelegate.
62 // Used to keep DynamicMethods alive.
63 private MethodInfo original_method_info;
66 protected Delegate (object target, string method)
69 throw new ArgumentNullException ("target");
72 throw new ArgumentNullException ("method");
74 this.target_type = null;
75 this.method_ptr = IntPtr.Zero;
76 this.m_target = target;
77 this.method_name = method;
80 protected Delegate (Type target, string method)
83 throw new ArgumentNullException ("target");
86 throw new ArgumentNullException ("method");
88 this.target_type = target;
89 this.method_ptr = IntPtr.Zero;
91 this.method_name = method;
94 public MethodInfo Method {
96 if (method_info != null) {
99 if (method != IntPtr.Zero)
100 method_info = (MethodInfo)MethodBase.GetMethodFromHandle (new RuntimeMethodHandle (method));
106 public object Target {
116 [MethodImplAttribute (MethodImplOptions.InternalCall)]
117 internal static extern Delegate CreateDelegate_internal (Type type, object target, MethodInfo info);
119 [MethodImplAttribute (MethodImplOptions.InternalCall)]
120 internal extern void SetMulticastInvoke ();
122 private static bool arg_type_match (Type delArgType, Type argType) {
123 bool match = delArgType == argType;
126 // Delegate contravariance
128 if (!delArgType.IsValueType && (delArgType != typeof (ValueType)) && (argType.IsAssignableFrom (delArgType)))
135 private static bool return_type_match (Type delReturnType, Type returnType) {
136 bool returnMatch = returnType == delReturnType;
140 // Delegate covariance
141 if (!delReturnType.IsValueType && (delReturnType != typeof (ValueType)) && (delReturnType.IsAssignableFrom (returnType)))
150 public static Delegate CreateDelegate (Type type, object firstArgument, MethodInfo method, bool throwOnBindFailure)
152 internal static Delegate CreateDelegate (Type type, object target, MethodInfo method, bool throwOnBindFailure)
156 // The name of the parameter changed in 2.0
157 object target = firstArgument;
161 throw new ArgumentNullException ("type");
164 throw new ArgumentNullException ("method");
166 if (!type.IsSubclassOf (typeof (MulticastDelegate)))
167 throw new ArgumentException ("type is not a subclass of Multicastdelegate");
169 if ((target == null) && !method.IsStatic) {
170 if (throwOnBindFailure)
171 throw new ArgumentException ("The method should be static.", "method");
177 MethodInfo invoke = type.GetMethod ("Invoke");
179 if (!return_type_match (invoke.ReturnType, method.ReturnType))
180 if (throwOnBindFailure)
181 throw new ArgumentException ("method return type is incompatible");
185 // FIXME: Figure out how net 1.1 works
187 ParameterInfo[] delargs = invoke.GetParameters ();
188 ParameterInfo[] args = method.GetParameters ();
192 if (target != null) {
193 // delegate closed over target
194 if (!method.IsStatic)
195 // target is passed as this
196 argLengthMatch = (args.Length == delargs.Length);
198 // target is passed as the first argument to the static method
199 argLengthMatch = (args.Length == delargs.Length + 1);
201 if (!method.IsStatic)
203 // Net 2.0 feature. The first argument of the delegate is passed
204 // as the 'this' argument to the method.
206 argLengthMatch = (args.Length + 1 == delargs.Length);
208 argLengthMatch = (args.Length == delargs.Length);
211 if (throwOnBindFailure)
212 throw new ArgumentException ("method argument length mismatch");
217 if (target != null) {
218 if (!method.IsStatic) {
219 argsMatch = arg_type_match (target.GetType (), method.DeclaringType);
220 for (int i = 0; i < args.Length; i++)
221 argsMatch &= arg_type_match (delargs [i].ParameterType, args [i].ParameterType);
223 argsMatch = arg_type_match (target.GetType (), args [0].ParameterType);
224 for (int i = 1; i < args.Length; i++)
225 argsMatch &= arg_type_match (delargs [i - 1].ParameterType, args [i].ParameterType);
228 if (!method.IsStatic) {
229 // The first argument should match this
230 argsMatch = arg_type_match (delargs [0].ParameterType, method.DeclaringType);
231 for (int i = 0; i < args.Length; i++)
232 argsMatch &= arg_type_match (delargs [i + 1].ParameterType, args [i].ParameterType);
235 for (int i = 0; i < args.Length; i++)
236 argsMatch &= arg_type_match (delargs [i].ParameterType, args [i].ParameterType);
241 if (throwOnBindFailure)
242 throw new ArgumentException ("method arguments are incompatible");
247 Delegate d = CreateDelegate_internal (type, target, method);
248 d.original_method_info = method;
257 static Delegate CreateDelegate (Type type, object target, MethodInfo method) {
258 return CreateDelegate (type, target, method, true);
266 static Delegate CreateDelegate (Type type, MethodInfo method, bool throwOnBindFailure)
268 return CreateDelegate (type, null, method, throwOnBindFailure);
271 public static Delegate CreateDelegate (Type type, MethodInfo method) {
272 return CreateDelegate (type, method, true);
275 public static Delegate CreateDelegate (Type type, object target, string method)
277 return CreateDelegate(type, target, method, false);
280 static MethodInfo GetCandidateMethod (Type type, Type target, string method, BindingFlags bflags, bool ignoreCase, bool throwOnBindFailure)
283 throw new ArgumentNullException ("type");
286 throw new ArgumentNullException ("method");
288 if (!type.IsSubclassOf (typeof (MulticastDelegate)))
289 throw new ArgumentException ("type is not subclass of MulticastDelegate.");
291 MethodInfo invoke = type.GetMethod ("Invoke");
292 ParameterInfo [] delargs = invoke.GetParameters ();
293 Type[] delargtypes = new Type [delargs.Length];
295 for (int i=0; i<delargs.Length; i++)
296 delargtypes [i] = delargs [i].ParameterType;
299 * FIXME: we should check the caller has reflection permission
300 * or if it lives in the same assembly...
304 * since we need to walk the inheritance chain anyway to
305 * find private methods, adjust the bindingflags to ignore
308 BindingFlags flags = BindingFlags.ExactBinding |
309 BindingFlags.Public | BindingFlags.NonPublic |
310 BindingFlags.DeclaredOnly | bflags;
313 flags |= BindingFlags.IgnoreCase;
315 MethodInfo info = null;
317 for (Type targetType = target; targetType != null; targetType = targetType.BaseType) {
318 MethodInfo mi = targetType.GetMethod (method, flags,
319 null, delargtypes, new ParameterModifier [0]);
320 if (mi != null && return_type_match (invoke.ReturnType, mi.ReturnType)) {
327 if (throwOnBindFailure)
328 throw new ArgumentException ("Couldn't bind to method '" + method + "'.");
341 static Delegate CreateDelegate (Type type, Type target, string method, bool ignoreCase, bool throwOnBindFailure)
344 throw new ArgumentNullException ("target");
346 MethodInfo info = GetCandidateMethod (type, target, method,
347 BindingFlags.Static, ignoreCase, throwOnBindFailure);
351 return CreateDelegate_internal (type, null, info);
354 public static Delegate CreateDelegate (Type type, Type target, string method) {
355 return CreateDelegate (type, target, method, false, true);
359 public static Delegate CreateDelegate (Type type, Type target, string method, bool ignoreCase) {
360 return CreateDelegate (type, target, method, ignoreCase, true);
369 static Delegate CreateDelegate (Type type, object target, string method, bool ignoreCase, bool throwOnBindFailure)
372 throw new ArgumentNullException ("target");
374 MethodInfo info = GetCandidateMethod (type, target.GetType (), method,
375 BindingFlags.Instance, ignoreCase, throwOnBindFailure);
379 return CreateDelegate_internal (type, target, info);
382 public static Delegate CreateDelegate (Type type, object target, string method, bool ignoreCase) {
383 return CreateDelegate (type, target, method, ignoreCase, true);
387 public object DynamicInvoke (params object[] args)
389 public object DynamicInvoke (object[] args)
392 return DynamicInvokeImpl (args);
395 protected virtual object DynamicInvokeImpl (object[] args)
397 if (Method == null) {
398 Type[] mtypes = new Type [args.Length];
399 for (int i = 0; i < args.Length; ++i) {
400 mtypes [i] = args [i].GetType ();
402 method_info = m_target.GetType ().GetMethod (method_name, mtypes);
406 if ((m_target != null) && Method.IsStatic) {
407 // The delegate is bound to m_target
409 object[] newArgs = new object [args.Length + 1];
410 args.CopyTo (newArgs, 1);
411 newArgs [0] = m_target;
414 args = new object [] { m_target };
416 return Method.Invoke (null, args);
420 return Method.Invoke (m_target, args);
423 public virtual object Clone ()
425 return MemberwiseClone ();
428 public override bool Equals (object obj)
430 Delegate d = obj as Delegate;
435 // Do not compare method_ptr, since it can point to a trampoline
436 if ((d.target_type == target_type) && (d.m_target == m_target) &&
437 (d.method_name == method_name) && (d.method == method))
443 public override int GetHashCode ()
445 // FIXME: Sync with Equals above
446 return (int)method_ptr;
449 protected virtual MethodInfo GetMethodImpl ()
454 // This is from ISerializable
455 public virtual void GetObjectData (SerializationInfo info, StreamingContext context)
457 DelegateSerializationHolder.GetDelegateData (this, info, context);
460 public virtual Delegate[] GetInvocationList()
462 return new Delegate[] { this };
466 /// Returns a new MulticastDelegate holding the
467 /// concatenated invocation lists of MulticastDelegates a and b
469 public static Delegate Combine (Delegate a, Delegate b)
479 if (a.GetType () != b.GetType ())
480 throw new ArgumentException (Locale.GetText ("Incompatible Delegate Types."));
482 return a.CombineImpl (b);
486 /// Returns a new MulticastDelegate holding the
487 /// concatenated invocation lists of an Array of MulticastDelegates
490 [System.Runtime.InteropServices.ComVisible (true)]
491 public static Delegate Combine (params Delegate[] delegates)
493 public static Delegate Combine (Delegate[] delegates)
496 if (delegates == null)
499 Delegate retval = null;
501 foreach (Delegate next in delegates)
502 retval = Combine (retval, next);
507 protected virtual Delegate CombineImpl (Delegate d)
509 throw new MulticastNotSupportedException (String.Empty);
512 public static Delegate Remove (Delegate source, Delegate value)
517 return source.RemoveImpl (value);
520 protected virtual Delegate RemoveImpl (Delegate d)
528 public static Delegate RemoveAll (Delegate source, Delegate value)
530 Delegate tmp = source;
531 while ((source = Delegate.Remove (source, value)) != tmp)
537 public static bool operator == (Delegate a, Delegate b)
539 if ((object)a == null) {
540 if ((object)b == null)
543 } else if ((object) b == null)
549 public static bool operator != (Delegate a, Delegate b)