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 [StructLayout (LayoutKind.Sequential)]
53 public abstract class Delegate : ICloneable, ISerializable
55 #region Sync with object-internals.h
56 #pragma warning disable 169, 414, 649
57 private IntPtr method_ptr;
58 private IntPtr invoke_impl;
59 private object m_target;
60 private IntPtr method;
61 private IntPtr delegate_trampoline;
62 private IntPtr method_code;
63 private MethodInfo method_info;
65 // Keep a ref of the MethodInfo passed to CreateDelegate.
66 // Used to keep DynamicMethods alive.
67 private MethodInfo original_method_info;
69 private DelegateData data;
70 #pragma warning restore 169, 414, 649
73 protected Delegate (object target, string method)
76 throw new ArgumentNullException ("target");
79 throw new ArgumentNullException ("method");
81 this.m_target = target;
82 this.data = new DelegateData ();
83 this.data.method_name = method;
86 protected Delegate (Type target, string method)
89 throw new ArgumentNullException ("target");
92 throw new ArgumentNullException ("method");
94 this.data = new DelegateData ();
95 this.data.method_name = method;
96 this.data.target_type = target;
99 public MethodInfo Method {
101 if (method_info != null) {
104 if (method != IntPtr.Zero) {
105 method_info = (MethodInfo)MethodBase.GetMethodFromHandleNoGenericCheck (new RuntimeMethodHandle (method));
112 public object Target {
122 [MethodImplAttribute (MethodImplOptions.InternalCall)]
123 internal static extern Delegate CreateDelegate_internal (Type type, object target, MethodInfo info, bool throwOnBindFailure);
125 [MethodImplAttribute (MethodImplOptions.InternalCall)]
126 internal extern void SetMulticastInvoke ();
128 private static bool arg_type_match (Type delArgType, Type argType) {
129 bool match = delArgType == argType;
131 // Delegate contravariance
133 if (!argType.IsValueType && argType.IsAssignableFrom (delArgType))
138 if (delArgType.IsEnum && Enum.GetUnderlyingType (delArgType) == argType)
145 private static bool arg_type_match_this (Type delArgType, Type argType, bool boxedThis) {
147 if (argType.IsValueType)
148 match = delArgType.IsByRef && delArgType.GetElementType () == argType ||
149 (boxedThis && delArgType == argType);
151 match = delArgType == argType || argType.IsAssignableFrom (delArgType);
155 private static bool return_type_match (Type delReturnType, Type returnType) {
156 bool returnMatch = returnType == delReturnType;
159 // Delegate covariance
160 if (!returnType.IsValueType && delReturnType.IsAssignableFrom (returnType))
167 public static Delegate CreateDelegate (Type type, object firstArgument, MethodInfo method, bool throwOnBindFailure)
169 return CreateDelegate (type, firstArgument, method, throwOnBindFailure, true);
172 static Delegate CreateDelegate (Type type, object firstArgument, MethodInfo method, bool throwOnBindFailure, bool allowClosed)
174 // The name of the parameter changed in 2.0
175 object target = firstArgument;
178 throw new ArgumentNullException ("type");
181 throw new ArgumentNullException ("method");
183 if (!type.IsSubclassOf (typeof (MulticastDelegate)))
184 throw new ArgumentException ("type is not a subclass of Multicastdelegate");
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 ParameterInfo[] delargs = invoke.GetParametersInternal ();
195 ParameterInfo[] args = method.GetParametersInternal ();
199 if (target != null) {
200 // delegate closed over target
201 if (!method.IsStatic)
202 // target is passed as this
203 argLengthMatch = (args.Length == delargs.Length);
205 // target is passed as the first argument to the static method
206 argLengthMatch = (args.Length == delargs.Length + 1);
208 if (!method.IsStatic) {
210 // Net 2.0 feature. The first argument of the delegate is passed
211 // as the 'this' argument to the method.
213 argLengthMatch = (args.Length + 1 == delargs.Length);
216 // closed over a null reference
217 argLengthMatch = (args.Length == delargs.Length);
219 argLengthMatch = (args.Length == delargs.Length);
222 // closed over a null reference
223 argLengthMatch = args.Length == delargs.Length + 1;
227 if (throwOnBindFailure)
228 throw new ArgumentException ("method argument length mismatch");
233 if (target != null) {
234 if (!method.IsStatic) {
235 argsMatch = arg_type_match_this (target.GetType (), method.DeclaringType, true);
236 for (int i = 0; i < args.Length; i++)
237 argsMatch &= arg_type_match (delargs [i].ParameterType, args [i].ParameterType);
239 argsMatch = arg_type_match (target.GetType (), args [0].ParameterType);
240 for (int i = 1; i < args.Length; i++)
241 argsMatch &= arg_type_match (delargs [i - 1].ParameterType, args [i].ParameterType);
244 if (!method.IsStatic) {
245 if (args.Length + 1 == delargs.Length) {
246 // The first argument should match this
247 argsMatch = arg_type_match_this (delargs [0].ParameterType, method.DeclaringType, false);
248 for (int i = 0; i < args.Length; i++)
249 argsMatch &= arg_type_match (delargs [i + 1].ParameterType, args [i].ParameterType);
251 // closed over a null reference
252 argsMatch = allowClosed;
253 for (int i = 0; i < args.Length; i++)
254 argsMatch &= arg_type_match (delargs [i].ParameterType, args [i].ParameterType);
257 if (delargs.Length + 1 == args.Length) {
258 // closed over a null reference
259 argsMatch = !(args [0].ParameterType.IsValueType || args [0].ParameterType.IsByRef) && allowClosed;
260 for (int i = 0; i < delargs.Length; i++)
261 argsMatch &= arg_type_match (delargs [i].ParameterType, args [i + 1].ParameterType);
264 for (int i = 0; i < args.Length; i++)
265 argsMatch &= arg_type_match (delargs [i].ParameterType, args [i].ParameterType);
271 if (throwOnBindFailure)
272 throw new ArgumentException ("method arguments are incompatible");
276 Delegate d = CreateDelegate_internal (type, target, method, throwOnBindFailure);
278 d.original_method_info = method;
282 public static Delegate CreateDelegate (Type type, object firstArgument, MethodInfo method)
284 return CreateDelegate (type, firstArgument, method, true, true);
287 public static Delegate CreateDelegate (Type type, MethodInfo method, bool throwOnBindFailure)
289 return CreateDelegate (type, null, method, throwOnBindFailure, false);
292 public static Delegate CreateDelegate (Type type, MethodInfo method)
294 return CreateDelegate (type, method, true);
297 public static Delegate CreateDelegate (Type type, object target, string method)
299 return CreateDelegate (type, target, method, false);
302 static MethodInfo GetCandidateMethod (Type type, Type target, string method, BindingFlags bflags, bool ignoreCase, bool throwOnBindFailure)
305 throw new ArgumentNullException ("type");
308 throw new ArgumentNullException ("method");
310 if (!type.IsSubclassOf (typeof (MulticastDelegate)))
311 throw new ArgumentException ("type is not subclass of MulticastDelegate.");
313 MethodInfo invoke = type.GetMethod ("Invoke");
314 ParameterInfo [] delargs = invoke.GetParametersInternal ();
315 Type[] delargtypes = new Type [delargs.Length];
317 for (int i=0; i<delargs.Length; i++)
318 delargtypes [i] = delargs [i].ParameterType;
321 * FIXME: we should check the caller has reflection permission
322 * or if it lives in the same assembly...
326 * since we need to walk the inheritance chain anyway to
327 * find private methods, adjust the bindingflags to ignore
330 BindingFlags flags = BindingFlags.ExactBinding |
331 BindingFlags.Public | BindingFlags.NonPublic |
332 BindingFlags.DeclaredOnly | bflags;
335 flags |= BindingFlags.IgnoreCase;
337 MethodInfo info = null;
339 for (Type targetType = target; targetType != null; targetType = targetType.BaseType) {
340 MethodInfo mi = targetType.GetMethod (method, flags,
341 null, delargtypes, EmptyArray<ParameterModifier>.Value);
342 if (mi != null && return_type_match (invoke.ReturnType, mi.ReturnType)) {
349 if (throwOnBindFailure)
350 throw new ArgumentException ("Couldn't bind to method '" + method + "'.");
358 public static Delegate CreateDelegate (Type type, Type target, string method, bool ignoreCase, bool throwOnBindFailure)
361 throw new ArgumentNullException ("target");
363 MethodInfo info = GetCandidateMethod (type, target, method,
364 BindingFlags.Static, ignoreCase, throwOnBindFailure);
368 return CreateDelegate_internal (type, null, info, throwOnBindFailure);
371 public static Delegate CreateDelegate (Type type, Type target, string method) {
372 return CreateDelegate (type, target, method, false, true);
375 public static Delegate CreateDelegate (Type type, Type target, string method, bool ignoreCase) {
376 return CreateDelegate (type, target, method, ignoreCase, true);
379 public 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, throwOnBindFailure);
392 public static Delegate CreateDelegate (Type type, object target, string method, bool ignoreCase) {
393 return CreateDelegate (type, target, method, ignoreCase, true);
396 public object DynamicInvoke (params object[] args)
398 return DynamicInvokeImpl (args);
401 protected virtual object DynamicInvokeImpl (object[] args)
403 if (Method == null) {
404 Type[] mtypes = new Type [args.Length];
405 for (int i = 0; i < args.Length; ++i) {
406 mtypes [i] = args [i].GetType ();
408 method_info = m_target.GetType ().GetMethod (data.method_name, mtypes);
411 if (Method.IsStatic && (args != null ? args.Length : 0) == Method.GetParametersCount () - 1) {
412 // The delegate is bound to m_target
414 object[] newArgs = new object [args.Length + 1];
415 args.CopyTo (newArgs, 1);
416 newArgs [0] = m_target;
419 args = new object [] { m_target };
421 return Method.Invoke (null, args);
424 return Method.Invoke (m_target, args);
427 public virtual object Clone ()
429 return MemberwiseClone ();
432 internal bool Compare (Delegate d)
437 // Do not compare method_ptr, since it can point to a trampoline
438 if (d.m_target == m_target && d.method == method) {
439 if (d.data != null || data != null) {
441 if (d.data != null && data != null)
442 return (d.data.target_type == data.target_type && d.data.method_name == data.method_name);
452 public override bool Equals (object obj)
454 return Compare (obj as Delegate);
457 public override int GetHashCode ()
459 return method.GetHashCode () ^ (m_target != null ? m_target.GetHashCode () : 0);
462 protected virtual MethodInfo GetMethodImpl ()
467 // This is from ISerializable
468 public virtual void GetObjectData (SerializationInfo info, StreamingContext context)
470 DelegateSerializationHolder.GetDelegateData (this, info, context);
473 public virtual Delegate[] GetInvocationList()
475 return new Delegate[] { this };
479 /// Returns a new MulticastDelegate holding the
480 /// concatenated invocation lists of MulticastDelegates a and b
482 public static Delegate Combine (Delegate a, Delegate b)
492 if (a.GetType () != b.GetType ())
493 throw new ArgumentException (Locale.GetText ("Incompatible Delegate Types. First is {0} second is {1}.", a.GetType ().FullName, b.GetType ().FullName));
495 return a.CombineImpl (b);
499 /// Returns a new MulticastDelegate holding the
500 /// concatenated invocation lists of an Array of MulticastDelegates
502 [System.Runtime.InteropServices.ComVisible (true)]
503 public static Delegate Combine (params Delegate[] delegates)
505 if (delegates == null)
508 Delegate retval = null;
510 foreach (Delegate next in delegates)
511 retval = Combine (retval, next);
516 protected virtual Delegate CombineImpl (Delegate d)
518 throw new MulticastNotSupportedException (String.Empty);
521 public static Delegate Remove (Delegate source, Delegate value)
529 if (source.GetType () != value.GetType ())
530 throw new ArgumentException (Locale.GetText ("Incompatible Delegate Types. First is {0} second is {1}.", source.GetType ().FullName, value.GetType ().FullName));
532 return source.RemoveImpl (value);
535 protected virtual Delegate RemoveImpl (Delegate d)
543 public static Delegate RemoveAll (Delegate source, Delegate value)
545 Delegate tmp = source;
546 while ((source = Delegate.Remove (source, value)) != tmp)
552 public static bool operator == (Delegate d1, Delegate d2)
554 if ((object)d1 == null) {
555 if ((object)d2 == null)
558 } else if ((object) d2 == null)
561 return d1.Equals (d2);
564 public static bool operator != (Delegate d1, Delegate d2)
569 internal bool IsTransparentProxy ()
574 return RemotingServices.IsTransparentProxy (m_target);