@@ -35,7 +35,7 @@ internal class RuntimeProxy<T> : DispatchProxy where T : class
3535 /// <summary>
3636 /// Stores setup information including whether parameters use It.IsAny
3737 /// </summary>
38- private readonly Dictionary < string , ( Delegate behavior , bool [ ] isAnyMatcher ) > _setupInfo = [ ] ;
38+ private readonly Dictionary < string , ( Delegate behavior , bool [ ] isAnyMatcher , string signatureKey ) > _setupInfo = [ ] ;
3939
4040 private static readonly ConcurrentDictionary < Type , object > _anyMatchers = new ( ) ;
4141
@@ -63,7 +63,7 @@ internal class RuntimeProxy<T> : DispatchProxy where T : class
6363 var anyMatcherBehavior = FindMatchingBehaviorWithIsAny ( targetMethod ! , args ) ;
6464 if ( anyMatcherBehavior != null )
6565 {
66- return anyMatcherBehavior . Method . GetParameters ( ) . Length == 0 ?
66+ return anyMatcherBehavior . Method . GetParameters ( ) . Length == 0 ?
6767 anyMatcherBehavior . DynamicInvoke ( ) :
6868 anyMatcherBehavior . DynamicInvoke ( args ) ;
6969 }
@@ -82,7 +82,7 @@ internal class RuntimeProxy<T> : DispatchProxy where T : class
8282 var tArg = ret . GenericTypeArguments [ 0 ] ;
8383 return typeof ( Task ) . GetMethod ( nameof ( Task . FromResult ) ) !
8484 . MakeGenericMethod ( tArg )
85- . Invoke ( null , new object ? [ ] { GetDefault ( tArg ) } ) ;
85+ . Invoke ( null , [ GetDefault ( tArg ) ] ) ;
8686 }
8787 if ( ret == typeof ( ValueTask ) ) return default ( ValueTask ) ;
8888 if ( ret . IsGenericType && ret . GetGenericTypeDefinition ( ) == typeof ( ValueTask < > ) )
@@ -98,14 +98,14 @@ internal class RuntimeProxy<T> : DispatchProxy where T : class
9898 /// </summary>
9999 private Delegate ? FindMatchingBehaviorWithIsAny ( MethodInfo method , object ? [ ] args )
100100 {
101+ var key = SignatureKey ( method ) ;
101102 // Check all setup information entries for this method looking for IsAny matches
102103 foreach ( var kvp in _setupInfo )
103104 {
104- if ( ! kvp . Key . StartsWith ( method . Name + "(" ) )
105- continue ;
106-
107- var ( behavior , isAnyMatcher ) = kvp . Value ;
105+ var ( behavior , isAnyMatcher , signatureKey ) = kvp . Value ;
108106
107+ if ( ! signatureKey . Equals ( key ) )
108+ continue ;
109109 // Only check setups that have at least one IsAny
110110 if ( ! isAnyMatcher . Any ( x => x ) )
111111 continue ;
@@ -120,39 +120,6 @@ internal class RuntimeProxy<T> : DispatchProxy where T : class
120120 return null ;
121121 }
122122
123- /// <summary>
124- /// Finds a matching behavior for the given method and arguments,
125- /// considering It.IsAny matchers in the setup.
126- /// </summary>
127- private Delegate ? FindMatchingBehavior ( MethodInfo method , object ? [ ] args )
128- {
129- var methodParams = method . GetParameters ( ) ;
130- Delegate ? anyMatcherBehavior = null ;
131-
132- // Check all setup information entries for this method
133- foreach ( var kvp in _setupInfo )
134- {
135- if ( ! kvp . Key . StartsWith ( method . Name + "(" ) )
136- continue ;
137-
138- var ( behavior , isAnyMatcher ) = kvp . Value ;
139-
140- // Check if this setup matches the current invocation
141- if ( DoesSetupMatch ( args , isAnyMatcher ) )
142- {
143- // If all parameters are specific (not IsAny), this is an exact match - return immediately
144- if ( ! isAnyMatcher . Any ( x => x ) )
145- return behavior ;
146-
147- // If has IsAny matchers, save it as fallback
148- anyMatcherBehavior = behavior ;
149- }
150- }
151-
152- // Return the exact match if found, otherwise return the IsAny match
153- return anyMatcherBehavior ;
154- }
155-
156123 /// <summary>
157124 /// Determines if a setup matches the current invocation arguments.
158125 /// </summary>
@@ -174,6 +141,7 @@ private static bool DoesSetupMatch(object?[] invocationArgs, bool[] isAnyMatcher
174141 // The actual exact matching is done via the argument key comparison in _behaviors dictionary
175142 }
176143
144+
177145 return true ;
178146 }
179147
@@ -195,11 +163,12 @@ public void Setup(MethodInfo method, Delegate behavior)
195163 public void Setup ( MethodInfo method , object ? [ ] args , Delegate behavior )
196164 {
197165 var argKey = CreateArgumentKey ( method , args ) ;
166+ var signatureKey = SignatureKey ( method ) ;
198167 _behaviors [ argKey ] = behavior ;
199168
200169 // Store setup info to track which arguments are IsAny matchers
201170 var isAnyMatcher = args . Select ( arg => IsAnyMatcherInstance ( arg ) ) . ToArray ( ) ;
202- _setupInfo [ argKey ] = ( behavior , isAnyMatcher ) ;
171+ _setupInfo [ argKey ] = ( behavior , isAnyMatcher , signatureKey ) ;
203172 }
204173
205174 /// <summary>
@@ -264,12 +233,15 @@ private void ExecuteCallbacks(MethodInfo method, object?[] args)
264233
265234 private static string SignatureKey ( MethodInfo mi )
266235 {
236+ // take into account generic parameters
237+ var genericPart = mi . IsGenericMethod ? $ "<{ string . Join ( "," , mi . GetGenericArguments ( ) . Select ( ga => ga . FullName ) ) } >" : "" ;
267238 var pars = string . Join ( "," , mi . GetParameters ( ) . Select ( p => p . ParameterType . FullName ) ) ;
268- return $ "{ mi . Name } ({ pars } )";
239+ return $ "{ mi . Name } ({ pars } ){ genericPart } ";
269240 }
270241
271242 private static string CreateArgumentKey ( MethodInfo mi , object ? [ ] args )
272243 {
244+ var genericPart = mi . IsGenericMethod ? $ "<{ string . Join ( "," , mi . GetGenericArguments ( ) . Select ( ga => ga . FullName ) ) } >" : "" ;
273245 var pars = string . Join ( "," , mi . GetParameters ( ) . Select ( p => p . ParameterType . FullName ) ) ;
274246 var argValues = string . Join ( "," , args . Select ( a =>
275247 {
@@ -278,7 +250,7 @@ private static string CreateArgumentKey(MethodInfo mi, object?[] args)
278250 return "IsAny" ;
279251 return a ? . ToString ( ) ?? "null" ;
280252 } ) ) ;
281- return $ "{ mi . Name } ({ pars } )[{ argValues } ]";
253+ return $ "{ mi . Name } ({ pars } )[{ argValues } ]{ genericPart } ";
282254 }
283255
284256 private static object ? GetDefault ( Type t ) => t . IsValueType ? Activator . CreateInstance ( t ) : null ;
0 commit comments