@@ -279,6 +279,23 @@ internal Binding Bind(IntPtr inst, IntPtr args, IntPtr kw, MethodBase info)
279279 return Bind ( inst , args , kw , info , null ) ;
280280 }
281281
282+ private readonly struct MatchedMethod {
283+ public MatchedMethod ( int kwargsMatched , int defaultsNeeded , object [ ] margs , int outs , MethodBase mb )
284+ {
285+ KwargsMatched = kwargsMatched ;
286+ DefaultsNeeded = defaultsNeeded ;
287+ ManagedArgs = margs ;
288+ Outs = outs ;
289+ Method = mb ;
290+ }
291+
292+ public int KwargsMatched { get ; }
293+ public int DefaultsNeeded { get ; }
294+ public object [ ] ManagedArgs { get ; }
295+ public int Outs { get ; }
296+ public MethodBase Method { get ; }
297+ }
298+
282299 internal Binding Bind ( IntPtr inst , IntPtr args , IntPtr kw , MethodBase info , MethodInfo [ ] methodinfo )
283300 {
284301 // loop to find match, return invoker w/ or /wo error
@@ -311,6 +328,8 @@ internal Binding Bind(IntPtr inst, IntPtr args, IntPtr kw, MethodBase info, Meth
311328 _methods = GetMethods ( ) ;
312329 }
313330
331+ var argMatchedMethods = new List < MatchedMethod > ( _methods . Length ) ;
332+
314333 // TODO: Clean up
315334 foreach ( MethodBase mi in _methods )
316335 {
@@ -321,8 +340,10 @@ internal Binding Bind(IntPtr inst, IntPtr args, IntPtr kw, MethodBase info, Meth
321340 ParameterInfo [ ] pi = mi . GetParameters ( ) ;
322341 ArrayList defaultArgList ;
323342 bool paramsArray ;
343+ int kwargsMatched ;
344+ int defaultsNeeded ;
324345
325- if ( ! MatchesArgumentCount ( pynargs , pi , kwargDict , out paramsArray , out defaultArgList ) )
346+ if ( ! MatchesArgumentCount ( pynargs , pi , kwargDict , out paramsArray , out defaultArgList , out kwargsMatched , out defaultsNeeded ) )
326347 {
327348 continue ;
328349 }
@@ -336,6 +357,47 @@ internal Binding Bind(IntPtr inst, IntPtr args, IntPtr kw, MethodBase info, Meth
336357 continue ;
337358 }
338359
360+ var matchedMethod = new MatchedMethod ( kwargsMatched , defaultsNeeded , margs , outs , mi ) ;
361+ argMatchedMethods . Add ( matchedMethod ) ;
362+ }
363+ if ( argMatchedMethods . Count > 0 )
364+ {
365+ var bestKwargMatchCount = argMatchedMethods . Max ( x => x . KwargsMatched ) ;
366+ var fewestDefaultsRequired = argMatchedMethods . Where ( x => x . KwargsMatched == bestKwargMatchCount ) . Min ( x => x . DefaultsNeeded ) ;
367+
368+ int bestCount = 0 ;
369+ int bestMatchIndex = - 1 ;
370+
371+ for ( int index = 0 ; index < argMatchedMethods . Count ; index ++ )
372+ {
373+ var testMatch = argMatchedMethods [ index ] ;
374+ if ( testMatch . DefaultsNeeded == fewestDefaultsRequired && testMatch . KwargsMatched == bestKwargMatchCount )
375+ {
376+ bestCount ++ ;
377+ if ( bestMatchIndex == - 1 )
378+ bestMatchIndex = index ;
379+ }
380+ }
381+
382+ if ( bestCount > 1 && fewestDefaultsRequired > 0 )
383+ {
384+ // Best effort for determining method to match on gives multiple possible
385+ // matches and we need at least one default argument - bail from this point
386+ return null ;
387+ }
388+
389+ // If we're here either:
390+ // (a) There is only one best match
391+ // (b) There are multiple best matches but none of them require
392+ // default arguments
393+ // in the case of (a) we're done by default. For (b) regardless of which
394+ // method we choose, all arguments are specified _and_ can be converted
395+ // from python to C# so picking any will suffice
396+ MatchedMethod bestMatch = argMatchedMethods [ bestMatchIndex ] ;
397+ var margs = bestMatch . ManagedArgs ;
398+ var outs = bestMatch . Outs ;
399+ var mi = bestMatch . Method ;
400+
339401 object target = null ;
340402 if ( ! mi . IsStatic && inst != IntPtr . Zero )
341403 {
@@ -575,11 +637,16 @@ static Type TryComputeClrArgumentType(Type parameterType, IntPtr argument, bool
575637 static bool MatchesArgumentCount ( int positionalArgumentCount , ParameterInfo [ ] parameters ,
576638 Dictionary < string , IntPtr > kwargDict ,
577639 out bool paramsArray ,
578- out ArrayList defaultArgList )
640+ out ArrayList defaultArgList ,
641+ out int kwargsMatched ,
642+ out int defaultsNeeded )
579643 {
580644 defaultArgList = null ;
581645 var match = false ;
582646 paramsArray = parameters . Length > 0 ? Attribute . IsDefined ( parameters [ parameters . Length - 1 ] , typeof ( ParamArrayAttribute ) ) : false ;
647+ var kwargCount = kwargDict . Count ;
648+ kwargsMatched = 0 ;
649+ defaultsNeeded = 0 ;
583650
584651 if ( positionalArgumentCount == parameters . Length && kwargDict . Count == 0 )
585652 {
@@ -599,6 +666,7 @@ static bool MatchesArgumentCount(int positionalArgumentCount, ParameterInfo[] pa
599666 // no need to check for a default parameter, but put a null
600667 // placeholder in defaultArgList
601668 defaultArgList . Add ( null ) ;
669+ kwargsMatched ++ ;
602670 }
603671 else if ( parameters [ v ] . IsOptional )
604672 {
@@ -607,6 +675,7 @@ static bool MatchesArgumentCount(int positionalArgumentCount, ParameterInfo[] pa
607675 // The GetDefaultValue() extension method will return the value
608676 // to be passed in as the parameter value
609677 defaultArgList . Add ( parameters [ v ] . GetDefaultValue ( ) ) ;
678+ defaultsNeeded ++ ;
610679 }
611680 else if ( ! paramsArray )
612681 {
0 commit comments