1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
5 using System
.Reflection
;
6 using System
.Diagnostics
;
7 using CultureInfo
= System
.Globalization
.CultureInfo
;
16 partial class DefaultBinder
: Binder
18 // This method is passed a set of methods and must choose the best
19 // fit. The methods all have the same number of arguments and the object
20 // array args. On exit, this method will choice the best fit method
21 // and coerce the args to match that method. By match, we mean all primitive
22 // arguments are exact matchs and all object arguments are exact or subclasses
23 // of the target. If the target OR is an interface, the object must implement
24 // that interface. There are a couple of exceptions
25 // thrown when a method cannot be returned. If no method matchs the args and
26 // ArgumentException is thrown. If multiple methods match the args then
27 // an AmbiguousMatchException is thrown.
29 // The most specific match will be selected.
31 public sealed override MethodBase
BindToMethod(
32 BindingFlags bindingAttr
, MethodBase
[] match
, ref object?[] args
,
33 ParameterModifier
[]? modifiers
, CultureInfo
? cultureInfo
, string[]? names
, out object? state
)
35 if (match
== null || match
.Length
== 0)
36 throw new ArgumentException(SR
.Arg_EmptyArray
, nameof(match
));
38 MethodBase
?[] candidates
= (MethodBase
[])match
.Clone();
45 #region Map named parameters to candidate parameter positions
46 // We are creating an paramOrder array to act as a mapping
47 // between the order of the args and the actual order of the
48 // parameters in the method. This order may differ because
49 // named parameters (names) may change the order. If names
50 // is not provided, then we assume the default mapping (0,1,...)
51 int[][] paramOrder
= new int[candidates
.Length
][];
53 for (i
= 0; i
< candidates
.Length
; i
++)
55 ParameterInfo
[] par
= candidates
[i
]!.GetParametersNoCopy();
57 // args.Length + 1 takes into account the possibility of a last paramArray that can be omitted
58 paramOrder
[i
] = new int[(par
.Length
> args
.Length
) ? par
.Length
: args
.Length
];
63 for (j
= 0; j
< args
.Length
; j
++)
68 // Named parameters, reorder the mapping. If CreateParamOrder fails, it means that the method
69 // doesn't have a name that matchs one of the named parameters so we don't consider it any further.
70 if (!CreateParamOrder(paramOrder
[i
], par
, names
))
76 Type
[] paramArrayTypes
= new Type
[candidates
.Length
];
78 Type
[] argTypes
= new Type
[args
.Length
];
80 #region Cache the type of the provided arguments
81 // object that contain a null are treated as if they were typeless (but match either object
82 // references or value classes). We mark this condition by placing a null in the argTypes array.
83 for (i
= 0; i
< args
.Length
; i
++)
87 argTypes
[i
] = args
[i
]!.GetType();
92 // Find the method that matches...
94 bool defaultValueBinding
= ((bindingAttr
& BindingFlags
.OptionalParamBinding
) != 0);
98 #region Filter methods by parameter count and type
99 for (i
= 0; i
< candidates
.Length
; i
++)
101 paramArrayType
= null;
103 // If we have named parameters then we may have a hole in the candidates array.
104 if (candidates
[i
] == null)
107 // Validate the parameters.
108 ParameterInfo
[] par
= candidates
[i
]!.GetParametersNoCopy(); // TODO-NULLABLE: Indexer nullability tracked (https://github.com/dotnet/roslyn/issues/34644)
110 #region Match method by parameter count
113 #region No formal parameters
114 if (args
.Length
!= 0)
116 if ((candidates
[i
]!.CallingConvention
& CallingConventions
.VarArgs
) == 0) // TODO-NULLABLE: Indexer nullability tracked (https://github.com/dotnet/roslyn/issues/34644)
120 // This is a valid routine so we move it up the candidates list.
121 paramOrder
[CurIdx
] = paramOrder
[i
];
122 candidates
[CurIdx
++] = candidates
[i
];
127 else if (par
.Length
> args
.Length
)
129 #region Shortage of provided parameters
130 // If the number of parameters is greater than the number of args then
131 // we are in the situation were we may be using default values.
132 for (j
= args
.Length
; j
< par
.Length
- 1; j
++)
134 if (par
[j
].DefaultValue
== System
.DBNull
.Value
)
138 if (j
!= par
.Length
- 1)
141 if (par
[j
].DefaultValue
== System
.DBNull
.Value
)
143 if (!par
[j
].ParameterType
.IsArray
)
146 if (!par
[j
].IsDefined(typeof(ParamArrayAttribute
), true))
149 paramArrayType
= par
[j
].ParameterType
.GetElementType();
153 else if (par
.Length
< args
.Length
)
155 #region Excess provided parameters
156 // test for the ParamArray case
157 int lastArgPos
= par
.Length
- 1;
159 if (!par
[lastArgPos
].ParameterType
.IsArray
)
162 if (!par
[lastArgPos
].IsDefined(typeof(ParamArrayAttribute
), true))
165 if (paramOrder
[i
][lastArgPos
] != lastArgPos
)
168 paramArrayType
= par
[lastArgPos
].ParameterType
.GetElementType();
173 #region Test for paramArray, save paramArray type
174 int lastArgPos
= par
.Length
- 1;
176 if (par
[lastArgPos
].ParameterType
.IsArray
177 && par
[lastArgPos
].IsDefined(typeof(ParamArrayAttribute
), true)
178 && paramOrder
[i
][lastArgPos
] == lastArgPos
)
180 if (!par
[lastArgPos
].ParameterType
.IsAssignableFrom(argTypes
[lastArgPos
]))
181 paramArrayType
= par
[lastArgPos
].ParameterType
.GetElementType();
188 int argsToCheck
= (paramArrayType
!= null) ? par
.Length
- 1 : args
.Length
;
190 #region Match method by parameter type
191 for (j
= 0; j
< argsToCheck
; j
++)
193 #region Classic argument coersion checks
194 // get the formal type
195 pCls
= par
[j
].ParameterType
;
198 pCls
= pCls
.GetElementType()!;
200 // the type is the same
201 if (pCls
== argTypes
[paramOrder
[i
][j
]])
204 // a default value is available
205 if (defaultValueBinding
&& args
[paramOrder
[i
][j
]] == Type
.Missing
)
208 // the argument was null, so it matches with everything
209 if (args
[paramOrder
[i
][j
]] == null)
212 // the type is Object, so it will match everything
213 if (pCls
== typeof(object))
216 // now do a "classic" type check
217 if (pCls
.IsPrimitive
)
219 if (argTypes
[paramOrder
[i
][j
]] == null || !CanChangePrimitive(args
[paramOrder
[i
][j
]]?.GetType(), pCls
))
226 if (argTypes
[paramOrder
[i
][j
]] == null)
229 if (!pCls
.IsAssignableFrom(argTypes
[paramOrder
[i
][j
]]))
231 if (argTypes
[paramOrder
[i
][j
]].IsCOMObject
)
233 if (pCls
.IsInstanceOfType(args
[paramOrder
[i
][j
]]))
242 if (paramArrayType
!= null && j
== par
.Length
- 1)
244 #region Check that excess arguments can be placed in the param array
245 for (; j
< args
.Length
; j
++)
247 if (paramArrayType
.IsPrimitive
)
249 if (argTypes
[j
] == null || !CanChangePrimitive(args
[j
]?.GetType(), paramArrayType
))
254 if (argTypes
[j
] == null)
257 if (!paramArrayType
.IsAssignableFrom(argTypes
[j
]))
259 if (argTypes
[j
].IsCOMObject
)
261 if (paramArrayType
.IsInstanceOfType(args
[j
]))
273 if (j
== args
.Length
)
275 #region This is a valid routine so we move it up the candidates list
276 paramOrder
[CurIdx
] = paramOrder
[i
];
277 paramArrayTypes
[CurIdx
] = paramArrayType
!;
278 candidates
[CurIdx
++] = candidates
[i
];
284 // If we didn't find a method
286 throw new MissingMethodException(SR
.MissingMember
);
290 #region Found only one method
293 state
= new BinderState((int[])paramOrder
[0].Clone(), args
.Length
, paramArrayTypes
[0] != null);
294 ReorderParams(paramOrder
[0], args
);
297 // If the parameters and the args are not the same length or there is a paramArray
298 // then we need to create a argument array.
299 ParameterInfo
[] parms
= candidates
[0]!.GetParametersNoCopy();
301 if (parms
.Length
== args
.Length
)
303 if (paramArrayTypes
[0] != null)
305 object[] objs
= new object[parms
.Length
];
306 int lastPos
= parms
.Length
- 1;
307 Array
.Copy(args
, 0, objs
, 0, lastPos
);
308 objs
[lastPos
] = Array
.CreateInstance(paramArrayTypes
[0], 1);
309 ((Array
)objs
[lastPos
]).SetValue(args
[lastPos
], 0);
313 else if (parms
.Length
> args
.Length
)
315 object?[] objs
= new object[parms
.Length
];
317 for (i
= 0; i
< args
.Length
; i
++)
320 for (; i
< parms
.Length
- 1; i
++)
321 objs
[i
] = parms
[i
].DefaultValue
;
323 if (paramArrayTypes
[0] != null)
324 objs
[i
] = Array
.CreateInstance(paramArrayTypes
[0], 0); // create an empty array for the
327 objs
[i
] = parms
[i
].DefaultValue
;
333 if ((candidates
[0]!.CallingConvention
& CallingConventions
.VarArgs
) == 0)
335 object[] objs
= new object[parms
.Length
];
336 int paramArrayPos
= parms
.Length
- 1;
337 Array
.Copy(args
, 0, objs
, 0, paramArrayPos
);
338 objs
[paramArrayPos
] = Array
.CreateInstance(paramArrayTypes
[0], args
.Length
- paramArrayPos
);
339 Array
.Copy(args
, paramArrayPos
, (System
.Array
)objs
[paramArrayPos
], 0, args
.Length
- paramArrayPos
);
345 return candidates
[0]!;
350 for (i
= 1; i
< CurIdx
; i
++)
352 #region Walk all of the methods looking the most specific method to invoke
353 int newMin
= FindMostSpecificMethod(candidates
[currentMin
]!, paramOrder
[currentMin
], paramArrayTypes
[currentMin
],
354 candidates
[i
]!, paramOrder
[i
], paramArrayTypes
[i
], argTypes
, args
);
360 else if (newMin
== 2)
369 throw new AmbiguousMatchException(SR
.Arg_AmbiguousMatchException
);
371 // Reorder (if needed)
374 state
= new BinderState((int[])paramOrder
[currentMin
].Clone(), args
.Length
, paramArrayTypes
[currentMin
] != null);
375 ReorderParams(paramOrder
[currentMin
], args
);
378 // If the parameters and the args are not the same length or there is a paramArray
379 // then we need to create a argument array.
380 ParameterInfo
[] parameters
= candidates
[currentMin
]!.GetParametersNoCopy();
381 if (parameters
.Length
== args
.Length
)
383 if (paramArrayTypes
[currentMin
] != null)
385 object[] objs
= new object[parameters
.Length
];
386 int lastPos
= parameters
.Length
- 1;
387 Array
.Copy(args
, 0, objs
, 0, lastPos
);
388 objs
[lastPos
] = Array
.CreateInstance(paramArrayTypes
[currentMin
], 1);
389 ((Array
)objs
[lastPos
]).SetValue(args
[lastPos
], 0);
393 else if (parameters
.Length
> args
.Length
)
395 object?[] objs
= new object[parameters
.Length
];
397 for (i
= 0; i
< args
.Length
; i
++)
400 for (; i
< parameters
.Length
- 1; i
++)
401 objs
[i
] = parameters
[i
].DefaultValue
;
403 if (paramArrayTypes
[currentMin
] != null)
405 objs
[i
] = Array
.CreateInstance(paramArrayTypes
[currentMin
], 0);
409 objs
[i
] = parameters
[i
].DefaultValue
;
416 if ((candidates
[currentMin
]!.CallingConvention
& CallingConventions
.VarArgs
) == 0)
418 object[] objs
= new object[parameters
.Length
];
419 int paramArrayPos
= parameters
.Length
- 1;
420 Array
.Copy(args
, 0, objs
, 0, paramArrayPos
);
421 objs
[paramArrayPos
] = Array
.CreateInstance(paramArrayTypes
[currentMin
], args
.Length
- paramArrayPos
);
422 Array
.Copy(args
, paramArrayPos
, (System
.Array
)objs
[paramArrayPos
], 0, args
.Length
- paramArrayPos
);
427 return candidates
[currentMin
]!;
430 // Given a set of fields that match the base criteria, select a field.
431 // if value is null then we have no way to select a field
432 public sealed override FieldInfo
BindToField(BindingFlags bindingAttr
, FieldInfo
[] match
, object value, CultureInfo
? cultureInfo
)
436 throw new ArgumentNullException(nameof(match
));
440 // Find the method that match...
445 FieldInfo
[] candidates
= (FieldInfo
[])match
.Clone();
447 // If we are a FieldSet, then use the value's type to disambiguate
448 if ((bindingAttr
& BindingFlags
.SetField
) != 0)
450 valueType
= value.GetType();
452 for (i
= 0; i
< candidates
.Length
; i
++)
454 Type pCls
= candidates
[i
].FieldType
;
455 if (pCls
== valueType
)
457 candidates
[CurIdx
++] = candidates
[i
];
460 if (value == Empty
.Value
)
462 // the object passed in was null which would match any non primitive non value type
465 candidates
[CurIdx
++] = candidates
[i
];
469 if (pCls
== typeof(object))
471 candidates
[CurIdx
++] = candidates
[i
];
474 if (pCls
.IsPrimitive
)
476 if (CanChangePrimitive(valueType
, pCls
))
478 candidates
[CurIdx
++] = candidates
[i
];
484 if (pCls
.IsAssignableFrom(valueType
))
486 candidates
[CurIdx
++] = candidates
[i
];
492 throw new MissingFieldException(SR
.MissingField
);
494 return candidates
[0];
497 // Walk all of the methods looking the most specific method to invoke
500 for (i
= 1; i
< CurIdx
; i
++)
502 int newMin
= FindMostSpecificField(candidates
[currentMin
], candidates
[i
]);
515 throw new AmbiguousMatchException(SR
.Arg_AmbiguousMatchException
);
516 return candidates
[currentMin
];
519 // Given a set of methods that match the base criteria, select a method based
520 // upon an array of types. This method should return null if no method matchs
522 public sealed override MethodBase
? SelectMethod(BindingFlags bindingAttr
, MethodBase
[] match
, Type
[] types
, ParameterModifier
[]? modifiers
)
527 Type
[] realTypes
= new Type
[types
.Length
];
528 for (i
= 0; i
< types
.Length
; i
++)
530 realTypes
[i
] = types
[i
].UnderlyingSystemType
;
531 if (!(realTypes
[i
].IsRuntimeImplemented() || realTypes
[i
] is SignatureType
))
532 throw new ArgumentException(SR
.Arg_MustBeType
, nameof(types
));
536 // We don't automatically jump out on exact match.
537 if (match
== null || match
.Length
== 0)
538 throw new ArgumentException(SR
.Arg_EmptyArray
, nameof(match
));
540 MethodBase
[] candidates
= (MethodBase
[])match
.Clone();
542 // Find all the methods that can be described by the types parameter.
543 // Remove all of them that cannot.
545 for (i
= 0; i
< candidates
.Length
; i
++)
547 ParameterInfo
[] par
= candidates
[i
].GetParametersNoCopy();
548 if (par
.Length
!= types
.Length
)
550 for (j
= 0; j
< types
.Length
; j
++)
552 Type pCls
= par
[j
].ParameterType
;
553 if (types
[j
].MatchesParameterTypeExactly(par
[j
]))
555 if (pCls
== typeof(object))
558 Type
? type
= types
[j
];
559 if (type
is SignatureType signatureType
)
561 if (!(candidates
[i
] is MethodInfo methodInfo
))
563 type
= signatureType
.TryResolveAgainstGenericMethod(methodInfo
);
568 if (pCls
.IsPrimitive
)
570 if (!type
.UnderlyingSystemType
.IsRuntimeImplemented() ||
571 !CanChangePrimitive(type
.UnderlyingSystemType
, pCls
.UnderlyingSystemType
))
576 if (!pCls
.IsAssignableFrom(type
))
580 if (j
== types
.Length
)
581 candidates
[CurIdx
++] = candidates
[i
];
586 return candidates
[0];
588 // Walk all of the methods looking the most specific method to invoke
591 int[] paramOrder
= new int[types
.Length
];
592 for (i
= 0; i
< types
.Length
; i
++)
594 for (i
= 1; i
< CurIdx
; i
++)
596 int newMin
= FindMostSpecificMethod(candidates
[currentMin
], paramOrder
, null, candidates
[i
], paramOrder
, null, types
, null);
610 throw new AmbiguousMatchException(SR
.Arg_AmbiguousMatchException
);
611 return candidates
[currentMin
];
614 // Given a set of properties that match the base criteria, select one.
615 public sealed override PropertyInfo
? SelectProperty(BindingFlags bindingAttr
, PropertyInfo
[] match
, Type
? returnType
,
616 Type
[]? indexes
, ParameterModifier
[]? modifiers
)
618 // Allow a null indexes array. But if it is not null, every element must be non-null as well.
621 foreach (Type index
in indexes
)
624 throw new ArgumentNullException(nameof(indexes
));
628 if (match
== null || match
.Length
== 0)
629 throw new ArgumentException(SR
.Arg_EmptyArray
, nameof(match
));
631 PropertyInfo
[] candidates
= (PropertyInfo
[])match
.Clone();
635 // Find all the properties that can be described by type indexes parameter
637 int indexesLength
= (indexes
!= null) ? indexes
.Length
: 0;
638 for (i
= 0; i
< candidates
.Length
; i
++)
642 ParameterInfo
[] par
= candidates
[i
].GetIndexParameters();
643 if (par
.Length
!= indexesLength
)
646 for (j
= 0; j
< indexesLength
; j
++)
648 Type pCls
= par
[j
].ParameterType
;
650 // If the classes exactly match continue
651 if (pCls
== indexes
[j
])
653 if (pCls
== typeof(object))
656 if (pCls
.IsPrimitive
)
658 if (!indexes
[j
].UnderlyingSystemType
.IsRuntimeImplemented() ||
659 !CanChangePrimitive(indexes
[j
].UnderlyingSystemType
, pCls
.UnderlyingSystemType
))
664 if (!pCls
.IsAssignableFrom(indexes
[j
]))
670 if (j
== indexesLength
)
672 if (returnType
!= null)
674 if (candidates
[i
].PropertyType
.IsPrimitive
)
676 if (!returnType
.UnderlyingSystemType
.IsRuntimeImplemented() ||
677 !CanChangePrimitive(returnType
.UnderlyingSystemType
, candidates
[i
].PropertyType
.UnderlyingSystemType
))
682 if (!candidates
[i
].PropertyType
.IsAssignableFrom(returnType
))
686 candidates
[CurIdx
++] = candidates
[i
];
692 return candidates
[0];
694 // Walk all of the properties looking the most specific method to invoke
697 int[] paramOrder
= new int[indexesLength
];
698 for (i
= 0; i
< indexesLength
; i
++)
700 for (i
= 1; i
< CurIdx
; i
++)
702 int newMin
= FindMostSpecificType(candidates
[currentMin
].PropertyType
, candidates
[i
].PropertyType
, returnType
);
703 if (newMin
== 0 && indexes
!= null)
704 newMin
= FindMostSpecific(candidates
[currentMin
].GetIndexParameters(),
707 candidates
[i
].GetIndexParameters(),
714 newMin
= FindMostSpecificProperty(candidates
[currentMin
], candidates
[i
]);
726 throw new AmbiguousMatchException(SR
.Arg_AmbiguousMatchException
);
727 return candidates
[currentMin
];
731 // The default binder doesn't support any change type functionality.
732 // This is because the default is built into the low level invoke code.
733 public override object ChangeType(object value, Type type
, CultureInfo
? cultureInfo
)
735 throw new NotSupportedException(SR
.NotSupported_ChangeType
);
738 public sealed override void ReorderArgumentArray(ref object?[] args
, object state
)
740 BinderState binderState
= (BinderState
)state
;
741 ReorderParams(binderState
._argsMap
, args
);
742 if (binderState
._isParamArray
)
744 int paramArrayPos
= args
.Length
- 1;
745 if (args
.Length
== binderState
._originalSize
)
747 args
[paramArrayPos
] = ((object[])args
[paramArrayPos
]!)[0];
751 // must be args.Length < state.originalSize
752 object[] newArgs
= new object[args
.Length
];
753 Array
.Copy(args
, 0, newArgs
, 0, paramArrayPos
);
754 for (int i
= paramArrayPos
, j
= 0; i
< newArgs
.Length
; i
++, j
++)
756 newArgs
[i
] = ((object[])args
[paramArrayPos
]!)[j
];
763 if (args
.Length
> binderState
._originalSize
)
765 object[] newArgs
= new object[binderState
._originalSize
];
766 Array
.Copy(args
, 0, newArgs
, 0, binderState
._originalSize
);
772 // Return any exact bindings that may exist. (This method is not defined on the
773 // Binder and is used by RuntimeType.)
774 public static MethodBase
? ExactBinding(MethodBase
[] match
, Type
[] types
, ParameterModifier
[]? modifiers
)
777 throw new ArgumentNullException(nameof(match
));
779 MethodBase
[] aExactMatches
= new MethodBase
[match
.Length
];
780 int cExactMatches
= 0;
782 for (int i
= 0; i
< match
.Length
; i
++)
784 ParameterInfo
[] par
= match
[i
].GetParametersNoCopy();
790 for (j
= 0; j
< types
.Length
; j
++)
792 Type pCls
= par
[j
].ParameterType
;
794 // If the classes exactly match continue
795 if (!pCls
.Equals(types
[j
]))
798 if (j
< types
.Length
)
801 // Add the exact match to the array of exact matches.
802 aExactMatches
[cExactMatches
] = match
[i
];
806 if (cExactMatches
== 0)
809 if (cExactMatches
== 1)
810 return aExactMatches
[0];
812 return FindMostDerivedNewSlotMeth(aExactMatches
, cExactMatches
);
815 // Return any exact bindings that may exist. (This method is not defined on the
816 // Binder and is used by RuntimeType.)
817 public static PropertyInfo
? ExactPropertyBinding(PropertyInfo
[] match
, Type
? returnType
, Type
[]? types
, ParameterModifier
[]? modifiers
)
820 throw new ArgumentNullException(nameof(match
));
822 PropertyInfo
? bestMatch
= null;
823 int typesLength
= (types
!= null) ? types
.Length
: 0;
824 for (int i
= 0; i
< match
.Length
; i
++)
826 ParameterInfo
[] par
= match
[i
].GetIndexParameters();
828 for (j
= 0; j
< typesLength
; j
++)
830 Type pCls
= par
[j
].ParameterType
;
832 // If the classes exactly match continue
833 if (pCls
!= types
![j
])
838 if (returnType
!= null && returnType
!= match
[i
].PropertyType
)
841 if (bestMatch
!= null)
842 throw new AmbiguousMatchException(SR
.Arg_AmbiguousMatchException
);
844 bestMatch
= match
[i
];
849 private static int FindMostSpecific(ParameterInfo
[] p1
, int[] paramOrder1
, Type
? paramArrayType1
,
850 ParameterInfo
[] p2
, int[] paramOrder2
, Type
? paramArrayType2
,
851 Type
[] types
, object?[]? args
)
853 // A method using params is always less specific than one not using params
854 if (paramArrayType1
!= null && paramArrayType2
== null) return 2;
855 if (paramArrayType2
!= null && paramArrayType1
== null) return 1;
857 // now either p1 and p2 both use params or neither does.
862 for (int i
= 0; i
< types
.Length
; i
++)
864 if (args
!= null && args
[i
] == Type
.Missing
)
869 // If a param array is present, then either
870 // the user re-ordered the parameters in which case
871 // the argument to the param array is either an array
872 // in which case the params is conceptually ignored and so paramArrayType1 == null
873 // or the argument to the param array is a single element
874 // in which case paramOrder[i] == p1.Length - 1 for that element
875 // or the user did not re-order the parameters in which case
876 // the paramOrder array could contain indexes larger than p.Length - 1 (see VSW 577286)
877 // so any index >= p.Length - 1 is being put in the param array
879 if (paramArrayType1
!= null && paramOrder1
[i
] >= p1
.Length
- 1)
880 c1
= paramArrayType1
;
882 c1
= p1
[paramOrder1
[i
]].ParameterType
;
884 if (paramArrayType2
!= null && paramOrder2
[i
] >= p2
.Length
- 1)
885 c2
= paramArrayType2
;
887 c2
= p2
[paramOrder2
[i
]].ParameterType
;
889 if (c1
== c2
) continue;
891 switch (FindMostSpecificType(c1
, c2
, types
[i
]))
894 case 1: p1Less
= true; break;
895 case 2: p2Less
= true; break;
899 // Two way p1Less and p2Less can be equal. All the arguments are the
900 // same they both equal false, otherwise there were things that both
901 // were the most specific type on....
902 if (p1Less
== p2Less
)
904 // if we cannot tell which is a better match based on parameter types (p1Less == p2Less),
905 // let's see which one has the most matches without using the params array (the longer one wins).
906 if (!p1Less
&& args
!= null)
908 if (p1
.Length
> p2
.Length
)
912 else if (p2
.Length
> p1
.Length
)
922 return p1Less
? 1 : 2;
926 private static int FindMostSpecificType(Type c1
, Type c2
, Type
? t
)
928 // If the two types are exact move on...
932 if (t
is SignatureType signatureType
)
934 if (signatureType
.MatchesExactly(c1
))
937 if (signatureType
.MatchesExactly(c2
))
952 if (c1
.IsByRef
|| c2
.IsByRef
)
954 if (c1
.IsByRef
&& c2
.IsByRef
)
956 c1
= c1
.GetElementType()!;
957 c2
= c2
.GetElementType()!;
961 if (c1
.GetElementType() == c2
)
964 c1
= c1
.GetElementType()!;
966 else // if (c2.IsByRef)
968 if (c2
.GetElementType() == c1
)
971 c2
= c2
.GetElementType()!;
975 if (c1
.IsPrimitive
&& c2
.IsPrimitive
)
977 c1FromC2
= CanChangePrimitive(c2
, c1
);
978 c2FromC1
= CanChangePrimitive(c1
, c2
);
982 c1FromC2
= c1
.IsAssignableFrom(c2
);
983 c2FromC1
= c2
.IsAssignableFrom(c1
);
986 if (c1FromC2
== c2FromC1
)
999 private static int FindMostSpecificMethod(MethodBase m1
, int[] paramOrder1
, Type
? paramArrayType1
,
1000 MethodBase m2
, int[] paramOrder2
, Type
? paramArrayType2
,
1001 Type
[] types
, object?[]? args
)
1003 // Find the most specific method based on the parameters.
1004 int res
= FindMostSpecific(m1
.GetParametersNoCopy(), paramOrder1
, paramArrayType1
,
1005 m2
.GetParametersNoCopy(), paramOrder2
, paramArrayType2
, types
, args
);
1007 // If the match was not ambigous then return the result.
1011 // Check to see if the methods have the exact same name and signature.
1012 if (CompareMethodSig(m1
, m2
))
1014 // Determine the depth of the declaring types for both methods.
1015 int hierarchyDepth1
= GetHierarchyDepth(m1
.DeclaringType
!);
1016 int hierarchyDepth2
= GetHierarchyDepth(m2
.DeclaringType
!);
1018 // The most derived method is the most specific one.
1019 if (hierarchyDepth1
== hierarchyDepth2
)
1023 else if (hierarchyDepth1
< hierarchyDepth2
)
1033 // The match is ambigous.
1037 private static int FindMostSpecificField(FieldInfo cur1
, FieldInfo cur2
)
1039 // Check to see if the fields have the same name.
1040 if (cur1
.Name
== cur2
.Name
)
1042 int hierarchyDepth1
= GetHierarchyDepth(cur1
.DeclaringType
!);
1043 int hierarchyDepth2
= GetHierarchyDepth(cur2
.DeclaringType
!);
1045 if (hierarchyDepth1
== hierarchyDepth2
)
1047 Debug
.Assert(cur1
.IsStatic
!= cur2
.IsStatic
, "hierarchyDepth1 == hierarchyDepth2");
1050 else if (hierarchyDepth1
< hierarchyDepth2
)
1056 // The match is ambigous.
1060 private static int FindMostSpecificProperty(PropertyInfo cur1
, PropertyInfo cur2
)
1062 // Check to see if the fields have the same name.
1063 if (cur1
.Name
== cur2
.Name
)
1065 int hierarchyDepth1
= GetHierarchyDepth(cur1
.DeclaringType
!);
1066 int hierarchyDepth2
= GetHierarchyDepth(cur2
.DeclaringType
!);
1068 if (hierarchyDepth1
== hierarchyDepth2
)
1072 else if (hierarchyDepth1
< hierarchyDepth2
)
1078 // The match is ambigous.
1082 public static bool CompareMethodSig(MethodBase m1
, MethodBase m2
)
1084 ParameterInfo
[] params1
= m1
.GetParametersNoCopy();
1085 ParameterInfo
[] params2
= m2
.GetParametersNoCopy();
1087 if (params1
.Length
!= params2
.Length
)
1090 int numParams
= params1
.Length
;
1091 for (int i
= 0; i
< numParams
; i
++)
1093 if (params1
[i
].ParameterType
!= params2
[i
].ParameterType
)
1100 private static int GetHierarchyDepth(Type t
)
1104 Type
? currentType
= t
;
1108 currentType
= currentType
.BaseType
;
1109 } while (currentType
!= null);
1114 internal static MethodBase
? FindMostDerivedNewSlotMeth(MethodBase
[] match
, int cMatches
)
1116 int deepestHierarchy
= 0;
1117 MethodBase
? methWithDeepestHierarchy
= null;
1119 for (int i
= 0; i
< cMatches
; i
++)
1121 // Calculate the depth of the hierarchy of the declaring type of the
1123 int currentHierarchyDepth
= GetHierarchyDepth(match
[i
].DeclaringType
!);
1125 // The two methods have the same name, signature, and hierarchy depth.
1126 // This can only happen if at least one is vararg or generic.
1127 if (currentHierarchyDepth
== deepestHierarchy
)
1129 throw new AmbiguousMatchException(SR
.Arg_AmbiguousMatchException
);
1132 // Check to see if this method is on the most derived class.
1133 if (currentHierarchyDepth
> deepestHierarchy
)
1135 deepestHierarchy
= currentHierarchyDepth
;
1136 methWithDeepestHierarchy
= match
[i
];
1140 return methWithDeepestHierarchy
;
1143 // This method will sort the vars array into the mapping order stored
1144 // in the paramOrder array.
1145 private static void ReorderParams(int[] paramOrder
, object?[] vars
)
1147 object?[] varsCopy
= new object[vars
.Length
];
1148 for (int i
= 0; i
< vars
.Length
; i
++)
1149 varsCopy
[i
] = vars
[i
];
1151 for (int i
= 0; i
< vars
.Length
; i
++)
1152 vars
[i
] = varsCopy
[paramOrder
[i
]];
1155 // This method will create the mapping between the Parameters and the underlying
1156 // data based upon the names array. The names array is stored in the same order
1157 // as the values and maps to the parameters of the method. We store the mapping
1158 // from the parameters to the names in the paramOrder array. All parameters that
1159 // don't have matching names are then stored in the array in order.
1160 private static bool CreateParamOrder(int[] paramOrder
, ParameterInfo
[] pars
, string[] names
)
1162 bool[] used
= new bool[pars
.Length
];
1164 // Mark which parameters have not been found in the names list
1165 for (int i
= 0; i
< pars
.Length
; i
++)
1167 // Find the parameters with names.
1168 for (int i
= 0; i
< names
.Length
; i
++)
1171 for (j
= 0; j
< pars
.Length
; j
++)
1173 if (names
[i
].Equals(pars
[j
].Name
))
1180 // This is an error condition. The name was not found. This
1181 // method must not match what we sent.
1182 if (j
== pars
.Length
)
1186 // Now we fill in the holes with the parameters that are unused.
1188 for (int i
= 0; i
< pars
.Length
; i
++)
1190 if (paramOrder
[i
] == -1)
1192 for (; pos
< pars
.Length
; pos
++)
1196 paramOrder
[i
] = pos
;
1206 // CanChangePrimitive
1207 // This will determine if the source can be converted to the target type
1208 internal static bool CanChangePrimitive(Type
? source
, Type
? target
)
1210 if ((source
== typeof(IntPtr
) && target
== typeof(IntPtr
)) ||
1211 (source
== typeof(UIntPtr
) && target
== typeof(UIntPtr
)))
1214 Primitives widerCodes
= s_primitiveConversions
[(int)(Type
.GetTypeCode(source
))];
1215 Primitives targetCode
= (Primitives
)(1 << (int)(Type
.GetTypeCode(target
)));
1217 return (widerCodes
& targetCode
) != 0;
1220 private static readonly Primitives
[] s_primitiveConversions
= {
1221 /* Empty */ 0, // not primitive
1222 /* Object */ 0, // not primitive
1223 /* DBNull */ 0, // not primitive
1224 /* Boolean */ Primitives
.Boolean
,
1225 /* Char */ Primitives
.Char
| Primitives
.UInt16
| Primitives
.UInt32
| Primitives
.Int32
| Primitives
.UInt64
| Primitives
.Int64
| Primitives
.Single
| Primitives
.Double
,
1226 /* SByte */ Primitives
.SByte
| Primitives
.Int16
| Primitives
.Int32
| Primitives
.Int64
| Primitives
.Single
| Primitives
.Double
,
1227 /* Byte */ Primitives
.Byte
| Primitives
.Char
| Primitives
.UInt16
| Primitives
.Int16
| Primitives
.UInt32
| Primitives
.Int32
| Primitives
.UInt64
| Primitives
.Int64
| Primitives
.Single
| Primitives
.Double
,
1228 /* Int16 */ Primitives
.Int16
| Primitives
.Int32
| Primitives
.Int64
| Primitives
.Single
| Primitives
.Double
,
1229 /* UInt16 */ Primitives
.UInt16
| Primitives
.UInt32
| Primitives
.Int32
| Primitives
.UInt64
| Primitives
.Int64
| Primitives
.Single
| Primitives
.Double
,
1230 /* Int32 */ Primitives
.Int32
| Primitives
.Int64
| Primitives
.Single
| Primitives
.Double
,
1231 /* UInt32 */ Primitives
.UInt32
| Primitives
.UInt64
| Primitives
.Int64
| Primitives
.Single
| Primitives
.Double
,
1232 /* Int64 */ Primitives
.Int64
| Primitives
.Single
| Primitives
.Double
,
1233 /* UInt64 */ Primitives
.UInt64
| Primitives
.Single
| Primitives
.Double
,
1234 /* Single */ Primitives
.Single
| Primitives
.Double
,
1235 /* Double */ Primitives
.Double
,
1236 /* Decimal */ Primitives
.Decimal
,
1237 /* DateTime */ Primitives
.DateTime
,
1239 /* String */ Primitives
.String
,
1243 private enum Primitives
1245 Boolean
= 1 << TypeCode
.Boolean
,
1246 Char
= 1 << TypeCode
.Char
,
1247 SByte
= 1 << TypeCode
.SByte
,
1248 Byte
= 1 << TypeCode
.Byte
,
1249 Int16
= 1 << TypeCode
.Int16
,
1250 UInt16
= 1 << TypeCode
.UInt16
,
1251 Int32
= 1 << TypeCode
.Int32
,
1252 UInt32
= 1 << TypeCode
.UInt32
,
1253 Int64
= 1 << TypeCode
.Int64
,
1254 UInt64
= 1 << TypeCode
.UInt64
,
1255 Single
= 1 << TypeCode
.Single
,
1256 Double
= 1 << TypeCode
.Double
,
1257 Decimal
= 1 << TypeCode
.Decimal
,
1258 DateTime
= 1 << TypeCode
.DateTime
,
1259 String
= 1 << TypeCode
.String
,
1262 internal class BinderState
1264 internal readonly int[] _argsMap
;
1265 internal readonly int _originalSize
;
1266 internal readonly bool _isParamArray
;
1268 internal BinderState(int[] argsMap
, int originalSize
, bool isParamArray
)
1271 _originalSize
= originalSize
;
1272 _isParamArray
= isParamArray
;