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;
42 /* Contains the rarely used fields of Delegate */
44 public Type target_type;
45 public string method_name;
49 [ClassInterface (ClassInterfaceType.AutoDual)]
52 [System.Runtime.InteropServices.ComVisible (true)]
55 public abstract class Delegate : ICloneable, ISerializable
57 #region Sync with object-internals.h
58 #pragma warning disable 169, 414, 649
59 private IntPtr method_ptr;
60 private IntPtr invoke_impl;
61 private object m_target;
62 private IntPtr method;
63 private IntPtr delegate_trampoline;
64 private IntPtr method_code;
65 private MethodInfo method_info;
67 // Keep a ref of the MethodInfo passed to CreateDelegate.
68 // Used to keep DynamicMethods alive.
69 private MethodInfo original_method_info;
71 private DelegateData data;
72 #pragma warning restore 169, 414, 649
75 protected Delegate (object target, string method)
78 throw new ArgumentNullException ("target");
81 throw new ArgumentNullException ("method");
83 this.m_target = target;
84 this.data = new DelegateData ();
85 this.data.method_name = method;
88 protected Delegate (Type target, string method)
91 throw new ArgumentNullException ("target");
94 throw new ArgumentNullException ("method");
96 this.data = new DelegateData ();
97 this.data.method_name = method;
98 this.data.target_type = target;
101 public MethodInfo Method {
103 if (method_info != null) {
106 if (method != IntPtr.Zero) {
107 method_info = (MethodInfo)MethodBase.GetMethodFromHandleNoGenericCheck (new RuntimeMethodHandle (method));
114 public object Target {
124 [MethodImplAttribute (MethodImplOptions.InternalCall)]
125 internal static extern Delegate CreateDelegate_internal (Type type, object target, MethodInfo info);
127 [MethodImplAttribute (MethodImplOptions.InternalCall)]
128 internal extern void SetMulticastInvoke ();
131 private static bool arg_type_match (Type delArgType, Type argType) {
132 bool match = delArgType == argType;
134 // Delegate contravariance
136 if (!delArgType.IsValueType && (delArgType != typeof (ValueType)) && (argType.IsAssignableFrom (delArgType)))
144 private static bool return_type_match (Type delReturnType, Type returnType) {
145 bool returnMatch = returnType == delReturnType;
149 // Delegate covariance
150 if (!delReturnType.IsValueType && (delReturnType != typeof (ValueType)) && (delReturnType.IsAssignableFrom (returnType)))
159 public static Delegate CreateDelegate (Type type, object firstArgument, MethodInfo method, bool throwOnBindFailure)
161 internal static Delegate CreateDelegate (Type type, object target, MethodInfo method, bool throwOnBindFailure)
165 // The name of the parameter changed in 2.0
166 object target = firstArgument;
170 throw new ArgumentNullException ("type");
173 throw new ArgumentNullException ("method");
175 if (!type.IsSubclassOf (typeof (MulticastDelegate)))
176 throw new ArgumentException ("type is not a subclass of Multicastdelegate");
178 if ((target == null) && !method.IsStatic) {
179 if (throwOnBindFailure)
180 throw new ArgumentException ("The method should be static.", "method");
186 MethodInfo invoke = type.GetMethod ("Invoke");
188 if (!return_type_match (invoke.ReturnType, method.ReturnType))
189 if (throwOnBindFailure)
190 throw new ArgumentException ("method return type is incompatible");
194 // FIXME: Figure out how net 1.1 works
196 ParameterInfo[] delargs = invoke.GetParameters ();
197 ParameterInfo[] args = method.GetParameters ();
201 if (target != null) {
202 // delegate closed over target
203 if (!method.IsStatic)
204 // target is passed as this
205 argLengthMatch = (args.Length == delargs.Length);
207 // target is passed as the first argument to the static method
208 argLengthMatch = (args.Length == delargs.Length + 1);
210 if (!method.IsStatic)
212 // Net 2.0 feature. The first argument of the delegate is passed
213 // as the 'this' argument to the method.
215 argLengthMatch = (args.Length + 1 == delargs.Length);
217 argLengthMatch = (args.Length == delargs.Length);
220 if (throwOnBindFailure)
221 throw new ArgumentException ("method argument length mismatch");
226 if (target != null) {
227 if (!method.IsStatic) {
228 argsMatch = arg_type_match (target.GetType (), method.DeclaringType);
229 for (int i = 0; i < args.Length; i++)
230 argsMatch &= arg_type_match (delargs [i].ParameterType, args [i].ParameterType);
232 argsMatch = arg_type_match (target.GetType (), args [0].ParameterType);
233 for (int i = 1; i < args.Length; i++)
234 argsMatch &= arg_type_match (delargs [i - 1].ParameterType, args [i].ParameterType);
237 if (!method.IsStatic) {
238 // The first argument should match this
239 argsMatch = arg_type_match (delargs [0].ParameterType, method.DeclaringType);
240 for (int i = 0; i < args.Length; i++)
241 argsMatch &= arg_type_match (delargs [i + 1].ParameterType, args [i].ParameterType);
244 for (int i = 0; i < args.Length; i++)
245 argsMatch &= arg_type_match (delargs [i].ParameterType, args [i].ParameterType);
250 if (throwOnBindFailure)
251 throw new ArgumentException ("method arguments are incompatible");
256 Delegate d = CreateDelegate_internal (type, target, method);
257 d.original_method_info = method;
262 public static Delegate CreateDelegate (Type type, object firstArgument, MethodInfo method) {
263 return CreateDelegate (type, firstArgument, method, true);
266 internal static Delegate CreateDelegate (Type type, object target, MethodInfo method) {
267 return CreateDelegate (type, target, method, true);
276 static Delegate CreateDelegate (Type type, MethodInfo method, bool throwOnBindFailure)
278 return CreateDelegate (type, null, method, throwOnBindFailure);
281 public static Delegate CreateDelegate (Type type, MethodInfo method) {
282 return CreateDelegate (type, method, true);
285 public static Delegate CreateDelegate (Type type, object target, string method)
287 return CreateDelegate(type, target, method, false);
290 static MethodInfo GetCandidateMethod (Type type, Type target, string method, BindingFlags bflags, bool ignoreCase, bool throwOnBindFailure)
293 throw new ArgumentNullException ("type");
296 throw new ArgumentNullException ("method");
298 if (!type.IsSubclassOf (typeof (MulticastDelegate)))
299 throw new ArgumentException ("type is not subclass of MulticastDelegate.");
301 MethodInfo invoke = type.GetMethod ("Invoke");
302 ParameterInfo [] delargs = invoke.GetParameters ();
303 Type[] delargtypes = new Type [delargs.Length];
305 for (int i=0; i<delargs.Length; i++)
306 delargtypes [i] = delargs [i].ParameterType;
309 * FIXME: we should check the caller has reflection permission
310 * or if it lives in the same assembly...
314 * since we need to walk the inheritance chain anyway to
315 * find private methods, adjust the bindingflags to ignore
318 BindingFlags flags = BindingFlags.ExactBinding |
319 BindingFlags.Public | BindingFlags.NonPublic |
320 BindingFlags.DeclaredOnly | bflags;
323 flags |= BindingFlags.IgnoreCase;
325 MethodInfo info = null;
327 for (Type targetType = target; targetType != null; targetType = targetType.BaseType) {
328 MethodInfo mi = targetType.GetMethod (method, flags,
329 null, delargtypes, new ParameterModifier [0]);
330 if (mi != null && return_type_match (invoke.ReturnType, mi.ReturnType)) {
337 if (throwOnBindFailure)
338 throw new ArgumentException ("Couldn't bind to method '" + method + "'.");
351 static Delegate CreateDelegate (Type type, Type target, string method, bool ignoreCase, bool throwOnBindFailure)
354 throw new ArgumentNullException ("target");
356 MethodInfo info = GetCandidateMethod (type, target, method,
357 BindingFlags.Static, ignoreCase, throwOnBindFailure);
361 return CreateDelegate_internal (type, null, info);
364 public static Delegate CreateDelegate (Type type, Type target, string method) {
365 return CreateDelegate (type, target, method, false, true);
369 public static Delegate CreateDelegate (Type type, Type target, string method, bool ignoreCase) {
370 return CreateDelegate (type, target, method, ignoreCase, true);
379 static Delegate CreateDelegate (Type type, object target, string method, bool ignoreCase, bool throwOnBindFailure)
382 throw new ArgumentNullException ("target");
384 MethodInfo info = GetCandidateMethod (type, target.GetType (), method,
385 BindingFlags.Instance, ignoreCase, throwOnBindFailure);
389 return CreateDelegate_internal (type, target, info);
392 public static Delegate CreateDelegate (Type type, object target, string method, bool ignoreCase) {
393 return CreateDelegate (type, target, method, ignoreCase, true);
397 public object DynamicInvoke (params object[] args)
399 public object DynamicInvoke (object[] args)
402 return DynamicInvokeImpl (args);
405 protected virtual object DynamicInvokeImpl (object[] args)
407 if (Method == null) {
408 Type[] mtypes = new Type [args.Length];
409 for (int i = 0; i < args.Length; ++i) {
410 mtypes [i] = args [i].GetType ();
412 method_info = m_target.GetType ().GetMethod (data.method_name, mtypes);
416 if ((m_target != null) && Method.IsStatic) {
417 // The delegate is bound to m_target
419 object[] newArgs = new object [args.Length + 1];
420 args.CopyTo (newArgs, 1);
421 newArgs [0] = m_target;
424 args = new object [] { m_target };
426 return Method.Invoke (null, args);
430 return Method.Invoke (m_target, args);
433 public virtual object Clone ()
435 return MemberwiseClone ();
438 public override bool Equals (object obj)
440 Delegate d = obj as Delegate;
445 // Do not compare method_ptr, since it can point to a trampoline
446 if (d.m_target == m_target && d.method == method) {
447 if (d.data != null || data != null) {
449 if (d.data != null && data != null)
450 return (d.data.target_type == data.target_type && d.data.method_name == data.method_name);
460 public override int GetHashCode ()
462 // FIXME: Sync with Equals above
463 return (int)method_ptr;
466 protected virtual MethodInfo GetMethodImpl ()
471 // This is from ISerializable
472 public virtual void GetObjectData (SerializationInfo info, StreamingContext context)
474 DelegateSerializationHolder.GetDelegateData (this, info, context);
477 public virtual Delegate[] GetInvocationList()
479 return new Delegate[] { this };
483 /// Returns a new MulticastDelegate holding the
484 /// concatenated invocation lists of MulticastDelegates a and b
486 public static Delegate Combine (Delegate a, Delegate b)
496 if (a.GetType () != b.GetType ())
497 throw new ArgumentException (Locale.GetText ("Incompatible Delegate Types."));
499 return a.CombineImpl (b);
503 /// Returns a new MulticastDelegate holding the
504 /// concatenated invocation lists of an Array of MulticastDelegates
507 [System.Runtime.InteropServices.ComVisible (true)]
508 public static Delegate Combine (params Delegate[] delegates)
510 public static Delegate Combine (Delegate[] delegates)
513 if (delegates == null)
516 Delegate retval = null;
518 foreach (Delegate next in delegates)
519 retval = Combine (retval, next);
524 protected virtual Delegate CombineImpl (Delegate d)
526 throw new MulticastNotSupportedException (String.Empty);
529 public static Delegate Remove (Delegate source, Delegate value)
534 return source.RemoveImpl (value);
537 protected virtual Delegate RemoveImpl (Delegate d)
545 public static Delegate RemoveAll (Delegate source, Delegate value)
547 Delegate tmp = source;
548 while ((source = Delegate.Remove (source, value)) != tmp)
554 public static bool operator == (Delegate d1, Delegate d2)
556 if ((object)d1 == null) {
557 if ((object)d2 == null)
560 } else if ((object) d2 == null)
563 return d1.Equals (d2);
566 public static bool operator != (Delegate d1, Delegate d2)