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.Remoting;
37 using System.Runtime.Serialization;
38 using System.Runtime.CompilerServices;
39 using System.Runtime.InteropServices;
43 /* Contains the rarely used fields of Delegate */
45 public Type target_type;
46 public string method_name;
49 [ClassInterface (ClassInterfaceType.AutoDual)]
50 [System.Runtime.InteropServices.ComVisible (true)]
52 public abstract class Delegate : ICloneable, ISerializable
54 #region Sync with object-internals.h
55 #pragma warning disable 169, 414, 649
56 private IntPtr method_ptr;
57 private IntPtr invoke_impl;
58 private object m_target;
59 private IntPtr method;
60 private IntPtr delegate_trampoline;
61 private IntPtr method_code;
62 private MethodInfo method_info;
64 // Keep a ref of the MethodInfo passed to CreateDelegate.
65 // Used to keep DynamicMethods alive.
66 private MethodInfo original_method_info;
68 private DelegateData data;
69 #pragma warning restore 169, 414, 649
72 protected Delegate (object target, string method)
75 throw new ArgumentNullException ("target");
78 throw new ArgumentNullException ("method");
80 this.m_target = target;
81 this.data = new DelegateData ();
82 this.data.method_name = method;
85 protected Delegate (Type target, string method)
88 throw new ArgumentNullException ("target");
91 throw new ArgumentNullException ("method");
93 this.data = new DelegateData ();
94 this.data.method_name = method;
95 this.data.target_type = target;
98 public MethodInfo Method {
100 if (method_info != null) {
103 if (method != IntPtr.Zero) {
104 method_info = (MethodInfo)MethodBase.GetMethodFromHandleNoGenericCheck (new RuntimeMethodHandle (method));
111 public object Target {
121 [MethodImplAttribute (MethodImplOptions.InternalCall)]
122 internal static extern Delegate CreateDelegate_internal (Type type, object target, MethodInfo info, bool throwOnBindFailure);
124 [MethodImplAttribute (MethodImplOptions.InternalCall)]
125 internal extern void SetMulticastInvoke ();
127 private static bool arg_type_match (Type delArgType, Type argType) {
128 bool match = delArgType == argType;
130 // Delegate contravariance
132 if (!argType.IsValueType && argType.IsAssignableFrom (delArgType))
139 private static bool return_type_match (Type delReturnType, Type returnType) {
140 bool returnMatch = returnType == delReturnType;
143 // Delegate covariance
144 if (!returnType.IsValueType && delReturnType.IsAssignableFrom (returnType))
151 public static Delegate CreateDelegate (Type type, object firstArgument, MethodInfo method, bool throwOnBindFailure)
153 return CreateDelegate (type, firstArgument, method, throwOnBindFailure, true);
156 public static Delegate CreateDelegate (Type type, object firstArgument, MethodInfo method, bool throwOnBindFailure, bool allowClosed)
158 // The name of the parameter changed in 2.0
159 object target = firstArgument;
162 throw new ArgumentNullException ("type");
165 throw new ArgumentNullException ("method");
167 if (!type.IsSubclassOf (typeof (MulticastDelegate)))
168 throw new ArgumentException ("type is not a subclass of Multicastdelegate");
170 MethodInfo invoke = type.GetMethod ("Invoke");
172 if (!return_type_match (invoke.ReturnType, method.ReturnType))
173 if (throwOnBindFailure)
174 throw new ArgumentException ("method return type is incompatible");
178 ParameterInfo[] delargs = invoke.GetParameters ();
179 ParameterInfo[] args = method.GetParameters ();
183 if (target != null) {
184 // delegate closed over target
185 if (!method.IsStatic)
186 // target is passed as this
187 argLengthMatch = (args.Length == delargs.Length);
189 // target is passed as the first argument to the static method
190 argLengthMatch = (args.Length == delargs.Length + 1);
192 if (!method.IsStatic) {
194 // Net 2.0 feature. The first argument of the delegate is passed
195 // as the 'this' argument to the method.
197 argLengthMatch = (args.Length + 1 == delargs.Length);
200 // closed over a null reference
201 argLengthMatch = (args.Length == delargs.Length);
203 argLengthMatch = (args.Length == delargs.Length);
206 // closed over a null reference
207 argLengthMatch = args.Length == delargs.Length + 1;
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 if (args.Length + 1 == delargs.Length) {
230 // The first argument should match this
231 argsMatch = arg_type_match (delargs [0].ParameterType, method.DeclaringType);
232 for (int i = 0; i < args.Length; i++)
233 argsMatch &= arg_type_match (delargs [i + 1].ParameterType, args [i].ParameterType);
235 // closed over a null reference
236 argsMatch = allowClosed;
237 for (int i = 0; i < args.Length; i++)
238 argsMatch &= arg_type_match (delargs [i].ParameterType, args [i].ParameterType);
241 if (delargs.Length + 1 == args.Length) {
242 // closed over a null reference
243 argsMatch = !args [0].ParameterType.IsValueType;
244 for (int i = 0; i < delargs.Length; i++)
245 argsMatch &= arg_type_match (delargs [i].ParameterType, args [i + 1].ParameterType);
248 for (int i = 0; i < args.Length; i++)
249 argsMatch &= arg_type_match (delargs [i].ParameterType, args [i].ParameterType);
255 if (throwOnBindFailure)
256 throw new ArgumentException ("method arguments are incompatible");
260 Delegate d = CreateDelegate_internal (type, target, method, throwOnBindFailure);
262 d.original_method_info = method;
266 public static Delegate CreateDelegate (Type type, object firstArgument, MethodInfo method)
268 return CreateDelegate (type, firstArgument, method, true, true);
271 public static Delegate CreateDelegate (Type type, MethodInfo method, bool throwOnBindFailure)
273 return CreateDelegate (type, null, method, throwOnBindFailure, false);
276 public static Delegate CreateDelegate (Type type, MethodInfo method)
278 return CreateDelegate (type, method, true);
281 public static Delegate CreateDelegate (Type type, object target, string method)
283 return CreateDelegate (type, target, method, false);
286 static MethodInfo GetCandidateMethod (Type type, Type target, string method, BindingFlags bflags, bool ignoreCase, bool throwOnBindFailure)
289 throw new ArgumentNullException ("type");
292 throw new ArgumentNullException ("method");
294 if (!type.IsSubclassOf (typeof (MulticastDelegate)))
295 throw new ArgumentException ("type is not subclass of MulticastDelegate.");
297 MethodInfo invoke = type.GetMethod ("Invoke");
298 ParameterInfo [] delargs = invoke.GetParameters ();
299 Type[] delargtypes = new Type [delargs.Length];
301 for (int i=0; i<delargs.Length; i++)
302 delargtypes [i] = delargs [i].ParameterType;
305 * FIXME: we should check the caller has reflection permission
306 * or if it lives in the same assembly...
310 * since we need to walk the inheritance chain anyway to
311 * find private methods, adjust the bindingflags to ignore
314 BindingFlags flags = BindingFlags.ExactBinding |
315 BindingFlags.Public | BindingFlags.NonPublic |
316 BindingFlags.DeclaredOnly | bflags;
319 flags |= BindingFlags.IgnoreCase;
321 MethodInfo info = null;
323 for (Type targetType = target; targetType != null; targetType = targetType.BaseType) {
324 MethodInfo mi = targetType.GetMethod (method, flags,
325 null, delargtypes, new ParameterModifier [0]);
326 if (mi != null && return_type_match (invoke.ReturnType, mi.ReturnType)) {
333 if (throwOnBindFailure)
334 throw new ArgumentException ("Couldn't bind to method '" + method + "'.");
342 public static Delegate CreateDelegate (Type type, Type target, string method, bool ignoreCase, bool throwOnBindFailure)
345 throw new ArgumentNullException ("target");
347 MethodInfo info = GetCandidateMethod (type, target, method,
348 BindingFlags.Static, ignoreCase, throwOnBindFailure);
352 return CreateDelegate_internal (type, null, info, throwOnBindFailure);
355 public static Delegate CreateDelegate (Type type, Type target, string method) {
356 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);
363 public static Delegate CreateDelegate (Type type, object target, string method, bool ignoreCase, bool throwOnBindFailure)
366 throw new ArgumentNullException ("target");
368 MethodInfo info = GetCandidateMethod (type, target.GetType (), method,
369 BindingFlags.Instance, ignoreCase, throwOnBindFailure);
373 return CreateDelegate_internal (type, target, info, throwOnBindFailure);
376 public static Delegate CreateDelegate (Type type, object target, string method, bool ignoreCase) {
377 return CreateDelegate (type, target, method, ignoreCase, true);
380 public object DynamicInvoke (params object[] args)
382 return DynamicInvokeImpl (args);
385 protected virtual object DynamicInvokeImpl (object[] args)
387 if (Method == null) {
388 Type[] mtypes = new Type [args.Length];
389 for (int i = 0; i < args.Length; ++i) {
390 mtypes [i] = args [i].GetType ();
392 method_info = m_target.GetType ().GetMethod (data.method_name, mtypes);
395 if ((m_target != null) && Method.IsStatic) {
396 // The delegate is bound to m_target
398 object[] newArgs = new object [args.Length + 1];
399 args.CopyTo (newArgs, 1);
400 newArgs [0] = m_target;
403 args = new object [] { m_target };
405 return Method.Invoke (null, args);
408 return Method.Invoke (m_target, args);
411 public virtual object Clone ()
413 return MemberwiseClone ();
416 public override bool Equals (object obj)
418 Delegate d = obj as Delegate;
423 // Do not compare method_ptr, since it can point to a trampoline
424 if (d.m_target == m_target && d.method == method) {
425 if (d.data != null || data != null) {
427 if (d.data != null && data != null)
428 return (d.data.target_type == data.target_type && d.data.method_name == data.method_name);
438 public override int GetHashCode ()
440 return method.GetHashCode () ^ (m_target != null ? m_target.GetHashCode () : 0);
443 protected virtual MethodInfo GetMethodImpl ()
448 // This is from ISerializable
449 public virtual void GetObjectData (SerializationInfo info, StreamingContext context)
451 DelegateSerializationHolder.GetDelegateData (this, info, context);
454 public virtual Delegate[] GetInvocationList()
456 return new Delegate[] { this };
460 /// Returns a new MulticastDelegate holding the
461 /// concatenated invocation lists of MulticastDelegates a and b
463 public static Delegate Combine (Delegate a, Delegate b)
473 if (a.GetType () != b.GetType ())
474 throw new ArgumentException (Locale.GetText ("Incompatible Delegate Types."));
476 return a.CombineImpl (b);
480 /// Returns a new MulticastDelegate holding the
481 /// concatenated invocation lists of an Array of MulticastDelegates
483 [System.Runtime.InteropServices.ComVisible (true)]
484 public static Delegate Combine (params Delegate[] delegates)
486 if (delegates == null)
489 Delegate retval = null;
491 foreach (Delegate next in delegates)
492 retval = Combine (retval, next);
497 protected virtual Delegate CombineImpl (Delegate d)
499 throw new MulticastNotSupportedException (String.Empty);
502 public static Delegate Remove (Delegate source, Delegate value)
510 if (source.GetType () != value.GetType ())
511 throw new ArgumentException ("Delegate type mismatch");
513 return source.RemoveImpl (value);
516 protected virtual Delegate RemoveImpl (Delegate d)
524 public static Delegate RemoveAll (Delegate source, Delegate value)
526 Delegate tmp = source;
527 while ((source = Delegate.Remove (source, value)) != tmp)
533 public static bool operator == (Delegate d1, Delegate d2)
535 if ((object)d1 == null) {
536 if ((object)d2 == null)
539 } else if ((object) d2 == null)
542 return d1.Equals (d2);
545 public static bool operator != (Delegate d1, Delegate d2)
550 internal bool IsTransparentProxy ()
552 return RemotingServices.IsTransparentProxy (m_target);