update readme (#21797)
[mono-project.git] / mcs / class / corlib / System / Delegate.cs
blobc42b60ca574412c6042a22ea90b2536cebfd3305
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 // Marek Safar (marek.safar@gmail.com)
9 //
10 // (C) Ximian, Inc. http://www.ximian.com
11 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
12 // Copyright 2014 Xamarin, Inc (http://www.xamarin.com)
14 // Permission is hereby granted, free of charge, to any person obtaining
15 // a copy of this software and associated documentation files (the
16 // "Software"), to deal in the Software without restriction, including
17 // without limitation the rights to use, copy, modify, merge, publish,
18 // distribute, sublicense, and/or sell copies of the Software, and to
19 // permit persons to whom the Software is furnished to do so, subject to
20 // the following conditions:
21 //
22 // The above copyright notice and this permission notice shall be
23 // included in all copies or substantial portions of the Software.
24 //
25 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
26 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
27 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
28 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
29 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
30 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
31 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
34 using System.Reflection;
35 using System.Runtime.Remoting;
36 using System.Runtime.Serialization;
37 using System.Runtime.CompilerServices;
38 using System.Runtime.InteropServices;
40 namespace System
42 /* Contains the rarely used fields of Delegate */
43 sealed class DelegateData
45 public Type target_type;
46 public string method_name;
47 public bool curried_first_arg;
50 [Serializable]
51 [StructLayout (LayoutKind.Sequential)]
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 extra_arg;
62 private IntPtr method_code;
63 private IntPtr interp_method;
64 private IntPtr interp_invoke_impl;
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;
73 private bool method_is_virtual;
74 #pragma warning restore 169, 414, 649
75 #endregion
77 protected Delegate (object target, string method)
79 if (target == null)
80 throw new ArgumentNullException ("target");
82 if (method == null)
83 throw new ArgumentNullException ("method");
85 this.m_target = target;
86 this.data = new DelegateData ();
87 this.data.method_name = method;
90 protected Delegate (Type target, string method)
92 if (target == null)
93 throw new ArgumentNullException ("target");
95 if (method == null)
96 throw new ArgumentNullException ("method");
98 this.data = new DelegateData ();
99 this.data.method_name = method;
100 this.data.target_type = target;
103 public MethodInfo Method {
104 get {
105 return GetMethodImpl ();
109 [MethodImplAttribute (MethodImplOptions.InternalCall)]
110 extern MethodInfo GetVirtualMethod_internal ();
112 public object Target {
113 get {
114 return m_target;
118 internal IntPtr GetNativeFunctionPointer () => method_ptr;
121 // Methods
124 [MethodImplAttribute (MethodImplOptions.InternalCall)]
125 internal static extern Delegate CreateDelegate_internal (Type type, object target, MethodInfo info, bool throwOnBindFailure);
127 private static bool arg_type_match (Type delArgType, Type argType) {
128 bool match = delArgType == argType;
130 // Delegate contravariance
131 if (!match) {
132 if (!argType.IsValueType && argType.IsAssignableFrom (delArgType))
133 match = true;
135 // enum basetypes
136 if (!match) {
137 if (delArgType.IsEnum && Enum.GetUnderlyingType (delArgType) == argType)
138 match = true;
139 else if (argType.IsEnum && Enum.GetUnderlyingType (argType) == delArgType)
140 match = true;
143 return match;
146 private static bool arg_type_match_this (Type delArgType, Type argType, bool boxedThis) {
147 bool match;
148 if (argType.IsValueType)
149 match = delArgType.IsByRef && delArgType.GetElementType () == argType ||
150 (boxedThis && delArgType == argType);
151 else
152 match = delArgType == argType || argType.IsAssignableFrom (delArgType);
154 return match;
156 private static bool return_type_match (Type delReturnType, Type returnType) {
157 bool returnMatch = returnType == delReturnType;
159 if (!returnMatch) {
160 // Delegate covariance
161 if (!returnType.IsValueType && delReturnType.IsAssignableFrom (returnType))
162 returnMatch = true;
165 return returnMatch;
168 public static Delegate CreateDelegate (Type type, object firstArgument, MethodInfo method, bool throwOnBindFailure)
170 return CreateDelegate (type, firstArgument, method, throwOnBindFailure, true);
173 static Delegate CreateDelegate (Type type, object firstArgument, MethodInfo method, bool throwOnBindFailure, bool allowClosed)
175 // The name of the parameter changed in 2.0
176 object target = firstArgument;
178 if (type == null)
179 throw new ArgumentNullException ("type");
181 if (method == null)
182 throw new ArgumentNullException ("method");
184 if (!type.IsSubclassOf (typeof (MulticastDelegate)))
185 throw new ArgumentException ("type is not a subclass of Multicastdelegate");
187 MethodInfo invoke = type.GetMethod ("Invoke");
189 if (!return_type_match (invoke.ReturnType, method.ReturnType)) {
190 if (throwOnBindFailure)
191 throw new ArgumentException ("method return type is incompatible");
192 else
193 return null;
196 ParameterInfo[] delargs = invoke.GetParametersInternal ();
197 ParameterInfo[] args = method.GetParametersInternal ();
199 bool argLengthMatch;
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);
206 else
207 // target is passed as the first argument to the static method
208 argLengthMatch = (args.Length == delargs.Length + 1);
209 } else {
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 if (!argLengthMatch)
218 // closed over a null reference
219 argLengthMatch = (args.Length == delargs.Length);
220 } else {
221 argLengthMatch = (args.Length == delargs.Length);
223 if (!argLengthMatch)
224 // closed over a null reference
225 argLengthMatch = args.Length == delargs.Length + 1;
228 if (!argLengthMatch) {
229 if (throwOnBindFailure)
230 throw new TargetParameterCountException ("Parameter count mismatch.");
231 else
232 return null;
235 bool argsMatch;
236 DelegateData delegate_data = new DelegateData ();
238 if (target != null) {
239 if (!method.IsStatic) {
240 argsMatch = arg_type_match_this (target.GetType (), method.DeclaringType, true);
241 for (int i = 0; i < args.Length; i++)
242 argsMatch &= arg_type_match (delargs [i].ParameterType, args [i].ParameterType);
243 } else {
244 argsMatch = arg_type_match (target.GetType (), args [0].ParameterType);
245 for (int i = 1; i < args.Length; i++)
246 argsMatch &= arg_type_match (delargs [i - 1].ParameterType, args [i].ParameterType);
248 delegate_data.curried_first_arg = true;
250 } else {
251 if (!method.IsStatic) {
252 if (args.Length + 1 == delargs.Length) {
253 // The first argument should match this
254 argsMatch = arg_type_match_this (delargs [0].ParameterType, method.DeclaringType, false);
255 for (int i = 0; i < args.Length; i++)
256 argsMatch &= arg_type_match (delargs [i + 1].ParameterType, args [i].ParameterType);
257 } else {
258 // closed over a null reference
259 argsMatch = allowClosed;
260 for (int i = 0; i < args.Length; i++)
261 argsMatch &= arg_type_match (delargs [i].ParameterType, args [i].ParameterType);
263 } else {
264 if (delargs.Length + 1 == args.Length) {
265 // closed over a null reference
266 argsMatch = !(args [0].ParameterType.IsValueType || args [0].ParameterType.IsByRef) && allowClosed;
267 for (int i = 0; i < delargs.Length; i++)
268 argsMatch &= arg_type_match (delargs [i].ParameterType, args [i + 1].ParameterType);
270 delegate_data.curried_first_arg = true;
271 } else {
272 argsMatch = true;
273 for (int i = 0; i < args.Length; i++)
274 argsMatch &= arg_type_match (delargs [i].ParameterType, args [i].ParameterType);
279 if (!argsMatch) {
280 if (throwOnBindFailure)
281 throw new ArgumentException ("method arguments are incompatible");
282 else
283 return null;
286 Delegate d = CreateDelegate_internal (type, target, method, throwOnBindFailure);
287 if (d != null)
288 d.original_method_info = method;
289 if (delegate_data != null)
290 d.data = delegate_data;
291 return d;
294 public static Delegate CreateDelegate (Type type, object firstArgument, MethodInfo method)
296 return CreateDelegate (type, firstArgument, method, true, true);
299 public static Delegate CreateDelegate (Type type, MethodInfo method, bool throwOnBindFailure)
301 return CreateDelegate (type, null, method, throwOnBindFailure, false);
304 public static Delegate CreateDelegate (Type type, MethodInfo method)
306 return CreateDelegate (type, method, true);
309 public static Delegate CreateDelegate (Type type, object target, string method)
311 return CreateDelegate (type, target, method, false);
314 static MethodInfo GetCandidateMethod (Type type, Type target, string method, BindingFlags bflags, bool ignoreCase, bool throwOnBindFailure)
316 if (type == null)
317 throw new ArgumentNullException ("type");
319 if (method == null)
320 throw new ArgumentNullException ("method");
322 if (!type.IsSubclassOf (typeof (MulticastDelegate)))
323 throw new ArgumentException ("type is not subclass of MulticastDelegate.");
325 MethodInfo invoke = type.GetMethod ("Invoke");
326 ParameterInfo [] delargs = invoke.GetParametersInternal ();
327 Type[] delargtypes = new Type [delargs.Length];
329 for (int i=0; i<delargs.Length; i++)
330 delargtypes [i] = delargs [i].ParameterType;
333 * FIXME: we should check the caller has reflection permission
334 * or if it lives in the same assembly...
338 * since we need to walk the inheritance chain anyway to
339 * find private methods, adjust the bindingflags to ignore
340 * inherited methods
342 BindingFlags flags = BindingFlags.ExactBinding |
343 BindingFlags.Public | BindingFlags.NonPublic |
344 BindingFlags.DeclaredOnly | bflags;
346 if (ignoreCase)
347 flags |= BindingFlags.IgnoreCase;
349 MethodInfo info = null;
351 for (Type targetType = target; targetType != null; targetType = targetType.BaseType) {
352 MethodInfo mi = targetType.GetMethod (method, flags,
353 null, delargtypes, Array.Empty<ParameterModifier>());
354 if (mi != null && return_type_match (invoke.ReturnType, mi.ReturnType)) {
355 info = mi;
356 break;
360 if (info == null) {
361 if (throwOnBindFailure)
362 throw new ArgumentException ("Couldn't bind to method '" + method + "'.");
363 else
364 return null;
367 return info;
370 public static Delegate CreateDelegate (Type type, Type target, string method, bool ignoreCase, bool throwOnBindFailure)
372 if (target == null)
373 throw new ArgumentNullException ("target");
375 MethodInfo info = GetCandidateMethod (type, target, method,
376 BindingFlags.Static, ignoreCase, throwOnBindFailure);
377 if (info == null)
378 return null;
380 return CreateDelegate_internal (type, null, info, throwOnBindFailure);
383 public static Delegate CreateDelegate (Type type, Type target, string method) {
384 return CreateDelegate (type, target, method, false, true);
387 public static Delegate CreateDelegate (Type type, Type target, string method, bool ignoreCase) {
388 return CreateDelegate (type, target, method, ignoreCase, true);
391 public static Delegate CreateDelegate (Type type, object target, string method, bool ignoreCase, bool throwOnBindFailure)
393 if (target == null)
394 throw new ArgumentNullException ("target");
396 MethodInfo info = GetCandidateMethod (type, target.GetType (), method,
397 BindingFlags.Instance, ignoreCase, throwOnBindFailure);
398 if (info == null)
399 return null;
401 return CreateDelegate_internal (type, target, info, throwOnBindFailure);
404 public static Delegate CreateDelegate (Type type, object target, string method, bool ignoreCase) {
405 return CreateDelegate (type, target, method, ignoreCase, true);
408 public object DynamicInvoke (params object[] args)
410 return DynamicInvokeImpl (args);
413 void InitializeDelegateData ()
415 DelegateData delegate_data = new DelegateData ();
416 if (method_info.IsStatic) {
417 if (m_target != null) {
418 delegate_data.curried_first_arg = true;
419 } else {
420 MethodInfo invoke = GetType ().GetMethod ("Invoke");
421 if (invoke.GetParametersCount () + 1 == method_info.GetParametersCount ())
422 delegate_data.curried_first_arg = true;
425 this.data = delegate_data;
428 protected virtual object DynamicInvokeImpl (object[] args)
430 if (Method == null) {
431 Type[] mtypes = new Type [args.Length];
432 for (int i = 0; i < args.Length; ++i) {
433 mtypes [i] = args [i].GetType ();
435 method_info = m_target.GetType ().GetMethod (data.method_name, mtypes);
438 var target = m_target;
439 if (this.data == null)
440 InitializeDelegateData ();
442 if (Method.IsStatic) {
444 // The delegate is bound to m_target
446 if (data.curried_first_arg) {
447 if (args == null) {
448 args = new [] { target };
449 } else {
450 Array.Resize (ref args, args.Length + 1);
451 Array.Copy (args, 0, args, 1, args.Length - 1);
452 args [0] = target;
455 target = null;
457 } else {
458 if (m_target == null && args != null && args.Length > 0) {
459 target = args [0];
460 Array.Copy (args, 1, args, 0, args.Length - 1);
461 Array.Resize (ref args, args.Length - 1);
465 return Method.Invoke (target, args);
468 public virtual object Clone ()
470 return MemberwiseClone ();
473 public override bool Equals (object obj)
475 Delegate d = obj as Delegate;
477 if (d == null)
478 return false;
480 // Do not compare method_ptr, since it can point to a trampoline
481 if (d.m_target == m_target && d.Method == Method) {
482 if (d.data != null || data != null) {
483 /* Uncommon case */
484 if (d.data != null && data != null)
485 return (d.data.target_type == data.target_type && d.data.method_name == data.method_name);
486 else {
487 if (d.data != null)
488 return d.data.target_type == null;
489 if (data != null)
490 return data.target_type == null;
491 return false;
494 return true;
497 return false;
500 public override int GetHashCode ()
502 MethodInfo m;
504 m = Method;
506 return (m != null ? m.GetHashCode () : GetType ().GetHashCode ()) ^ RuntimeHelpers.GetHashCode (m_target);
509 protected virtual MethodInfo GetMethodImpl ()
511 if (method_info != null) {
512 return method_info;
513 } else {
514 if (method != IntPtr.Zero) {
515 if (!method_is_virtual)
516 method_info = (MethodInfo)RuntimeMethodInfo.GetMethodFromHandleNoGenericCheck (new RuntimeMethodHandle (method));
517 else
518 method_info = GetVirtualMethod_internal ();
520 return method_info;
524 // This is from ISerializable
525 public virtual void GetObjectData (SerializationInfo info, StreamingContext context)
527 DelegateSerializationHolder.GetDelegateData (this, info, context);
530 public virtual Delegate[] GetInvocationList()
532 return new Delegate[] { this };
535 /// <symmary>
536 /// Returns a new MulticastDelegate holding the
537 /// concatenated invocation lists of MulticastDelegates a and b
538 /// </symmary>
539 public static Delegate Combine (Delegate a, Delegate b)
541 if (a == null)
542 return b;
544 if (b == null)
545 return a;
547 if (a.GetType () != b.GetType ())
548 throw new ArgumentException (string.Format ("Incompatible Delegate Types. First is {0} second is {1}.", a.GetType ().FullName, b.GetType ().FullName));
550 return a.CombineImpl (b);
553 /// <symmary>
554 /// Returns a new MulticastDelegate holding the
555 /// concatenated invocation lists of an Array of MulticastDelegates
556 /// </symmary>
557 [System.Runtime.InteropServices.ComVisible (true)]
558 public static Delegate Combine (params Delegate[] delegates)
560 if (delegates == null)
561 return null;
563 Delegate retval = null;
565 foreach (Delegate next in delegates)
566 retval = Combine (retval, next);
568 return retval;
571 protected virtual Delegate CombineImpl (Delegate d)
573 throw new MulticastNotSupportedException (String.Empty);
576 public static Delegate Remove (Delegate source, Delegate value)
578 if (source == null)
579 return null;
581 if (value == null)
582 return source;
584 if (source.GetType () != value.GetType ())
585 throw new ArgumentException (string.Format ("Incompatible Delegate Types. First is {0} second is {1}.", source.GetType ().FullName, value.GetType ().FullName));
587 return source.RemoveImpl (value);
590 protected virtual Delegate RemoveImpl (Delegate d)
592 if (this.Equals (d))
593 return null;
595 return this;
598 public static Delegate RemoveAll (Delegate source, Delegate value)
600 Delegate tmp = source;
601 while ((source = Delegate.Remove (source, value)) != tmp)
602 tmp = source;
604 return tmp;
607 public static bool operator == (Delegate d1, Delegate d2)
609 if ((object)d1 == null) {
610 if ((object)d2 == null)
611 return true;
612 return false;
613 } else if ((object) d2 == null)
614 return false;
616 return d1.Equals (d2);
619 public static bool operator != (Delegate d1, Delegate d2)
621 return !(d1 == d2);
624 internal bool IsTransparentProxy ()
626 #if !FEATURE_REMOTING
627 return false;
628 #else
629 return RemotingServices.IsTransparentProxy (m_target);
630 #endif
633 internal static Delegate CreateDelegateNoSecurityCheck (RuntimeType type, Object firstArgument, MethodInfo method)
635 return CreateDelegate_internal (type, firstArgument, method, true);
638 /* Internal call largely inspired from MS Delegate.InternalAllocLike */
639 [MethodImplAttribute(MethodImplOptions.InternalCall)]
640 internal extern static MulticastDelegate AllocDelegateLike_internal (Delegate d);