@@ -100,17 +100,7 @@ public Mock<T> Setup<TResult>(Expression<Func<T, TResult>> expression, Func<TRes
100100 public Mock < T > Setup < TResult , T1 > ( Expression < Func < T , TResult > > expression , Func < T1 , TResult > handler )
101101 {
102102 var ( method , args ) = ExtractMethod ( expression ) ;
103- var parameters = method . GetParameters ( ) ;
104-
105- // Create a delegate that matches the method signature and extracts only the first parameter
106- Delegate behavior = parameters . Length switch
107- {
108- 1 => new Func < T1 , TResult > ( handler ) ,
109- 2 => new Func < T1 , object ? , TResult > ( ( p1 , p2 ) => handler ( p1 ) ) ,
110- 3 => new Func < T1 , object ? , object ? , TResult > ( ( p1 , p2 , p3 ) => handler ( p1 ) ) ,
111- _ => new Func < TResult > ( ( ) => handler ( default ( T1 ) ! ) )
112- } ;
113-
103+ var behavior = CreatePartialHandlerDelegate ( method , handler , new [ ] { typeof ( T1 ) } , typeof ( TResult ) ) ;
114104 _proxy . Setup ( method , args , behavior ) ;
115105 return this ;
116106 }
@@ -136,16 +126,7 @@ public Mock<T> Setup<TResult, T1>(Expression<Func<T, TResult>> expression, Func<
136126 public Mock < T > Setup < TResult , T1 , T2 > ( Expression < Func < T , TResult > > expression , Func < T1 , T2 , TResult > handler )
137127 {
138128 var ( method , args ) = ExtractMethod ( expression ) ;
139- var parameters = method . GetParameters ( ) ;
140-
141- // Create a delegate that matches the method signature and extracts only the first two parameters
142- Delegate behavior = parameters . Length switch
143- {
144- 2 => new Func < T1 , T2 , TResult > ( handler ) ,
145- 3 => new Func < T1 , T2 , object ? , TResult > ( ( p1 , p2 , p3 ) => handler ( p1 , p2 ) ) ,
146- _ => new Func < TResult > ( ( ) => handler ( default ( T1 ) ! , default ( T2 ) ! ) )
147- } ;
148-
129+ var behavior = CreatePartialHandlerDelegate ( method , handler , new [ ] { typeof ( T1 ) , typeof ( T2 ) } , typeof ( TResult ) ) ;
149130 _proxy . Setup ( method , args , behavior ) ;
150131 return this ;
151132 }
@@ -172,15 +153,7 @@ public Mock<T> Setup<TResult, T1, T2>(Expression<Func<T, TResult>> expression, F
172153 public Mock < T > Setup < TResult , T1 , T2 , T3 > ( Expression < Func < T , TResult > > expression , Func < T1 , T2 , T3 , TResult > handler )
173154 {
174155 var ( method , args ) = ExtractMethod ( expression ) ;
175- var parameters = method . GetParameters ( ) ;
176-
177- // Create a delegate that matches the method signature and extracts only the first three parameters
178- Delegate behavior = parameters . Length switch
179- {
180- 3 => new Func < T1 , T2 , T3 , TResult > ( handler ) ,
181- _ => new Func < TResult > ( ( ) => handler ( default ( T1 ) ! , default ( T2 ) ! , default ( T3 ) ! ) )
182- } ;
183-
156+ var behavior = CreatePartialHandlerDelegate ( method , handler , new [ ] { typeof ( T1 ) , typeof ( T2 ) , typeof ( T3 ) } , typeof ( TResult ) ) ;
184157 _proxy . Setup ( method , args , behavior ) ;
185158 return this ;
186159 }
@@ -460,6 +433,134 @@ private static PropertyInfo ExtractProperty(LambdaExpression expr)
460433 throw new ArgumentException ( "Expression must be a property access" ) ;
461434 }
462435
436+ /// <summary>
437+ /// Creates a delegate that wraps a partial handler, extracting only the needed parameters from the method signature.
438+ /// </summary>
439+ /// <param name="method">The target method being mocked.</param>
440+ /// <param name="handler">The handler delegate that receives a subset of parameters.</param>
441+ /// <param name="handlerParamTypes">The types of parameters expected by the handler.</param>
442+ /// <param name="returnType">The return type of the method.</param>
443+ /// <returns>A delegate matching the full method signature that forwards to the partial handler.</returns>
444+ private static Delegate CreatePartialHandlerDelegate ( MethodInfo method , Delegate handler , Type [ ] handlerParamTypes , Type returnType )
445+ {
446+ var methodParams = method . GetParameters ( ) ;
447+
448+ // Validate that the method has at least as many parameters as the handler expects
449+ if ( methodParams . Length < handlerParamTypes . Length )
450+ {
451+ throw new ArgumentException (
452+ $ "The method '{ method . Name } ' has { methodParams . Length } parameter(s), but the handler expects { handlerParamTypes . Length } parameter(s). " +
453+ $ "The handler can only receive up to { methodParams . Length } parameter(s).",
454+ nameof ( handler ) ) ;
455+ }
456+
457+ // Validate type compatibility for each handler parameter
458+ for ( int i = 0 ; i < handlerParamTypes . Length ; i ++ )
459+ {
460+ var methodParamType = methodParams [ i ] . ParameterType ;
461+ var handlerParamType = handlerParamTypes [ i ] ;
462+
463+ if ( ! handlerParamType . IsAssignableFrom ( methodParamType ) && methodParamType != handlerParamType )
464+ {
465+ throw new ArgumentException (
466+ $ "Parameter type mismatch at position { i } : method '{ method . Name } ' has parameter type '{ methodParamType . Name } ' " +
467+ $ "but handler expects '{ handlerParamType . Name } '.",
468+ nameof ( handler ) ) ;
469+ }
470+ }
471+
472+ // If the method has the exact same number of parameters as the handler, just return the handler
473+ if ( methodParams . Length == handlerParamTypes . Length )
474+ {
475+ return handler ;
476+ }
477+
478+ // Build parameter expressions for the full method signature
479+ var methodParamExpressions = methodParams
480+ . Select ( p => Expression . Parameter ( p . ParameterType , p . Name ) )
481+ . ToArray ( ) ;
482+
483+ // Create expression to invoke the handler with only the first N parameters
484+ var handlerConstant = Expression . Constant ( handler ) ;
485+ var handlerParams = methodParamExpressions . Take ( handlerParamTypes . Length ) . ToArray ( ) ;
486+ var invokeExpression = Expression . Invoke ( handlerConstant , handlerParams ) ;
487+
488+ // Build the correct Func<> delegate type
489+ var delegateTypeArgs = methodParams . Select ( p => p . ParameterType ) . Append ( returnType ) . ToArray ( ) ;
490+ Type delegateType ;
491+
492+ if ( delegateTypeArgs . Length == 1 )
493+ {
494+ // Func<TResult> - only return type
495+ delegateType = typeof ( Func < > ) . MakeGenericType ( delegateTypeArgs ) ;
496+ }
497+ else
498+ {
499+ // Func<T1, ..., TN, TResult>
500+ var funcType = delegateTypeArgs . Length switch
501+ {
502+ 2 => typeof ( Func < , > ) ,
503+ 3 => typeof ( Func < , , > ) ,
504+ 4 => typeof ( Func < , , , > ) ,
505+ 5 => typeof ( Func < , , , , > ) ,
506+ 6 => typeof ( Func < , , , , , > ) ,
507+ 7 => typeof ( Func < , , , , , , > ) ,
508+ 8 => typeof ( Func < , , , , , , , > ) ,
509+ 9 => typeof ( Func < , , , , , , , , > ) ,
510+ 10 => typeof ( Func < , , , , , , , , , > ) ,
511+ 11 => typeof ( Func < , , , , , , , , , , > ) ,
512+ 12 => typeof ( Func < , , , , , , , , , , , > ) ,
513+ 13 => typeof ( Func < , , , , , , , , , , , , > ) ,
514+ 14 => typeof ( Func < , , , , , , , , , , , , , > ) ,
515+ 15 => typeof ( Func < , , , , , , , , , , , , , , > ) ,
516+ 16 => typeof ( Func < , , , , , , , , , , , , , , , > ) ,
517+ 17 => typeof ( Func < , , , , , , , , , , , , , , , , > ) ,
518+ _ => throw new NotSupportedException ( $ "Methods with more than 16 parameters are not supported.")
519+ } ;
520+ delegateType = funcType . MakeGenericType ( delegateTypeArgs ) ;
521+ }
522+
523+ // Create the lambda with the correct delegate type
524+ var lambdaExpression = Expression . Lambda ( delegateType , invokeExpression , methodParamExpressions ) ;
525+
526+ return lambdaExpression . Compile ( ) ;
527+ }
528+
529+ /// <summary>
530+ /// Creates an action delegate that wraps a partial handler for OnCall, extracting only the needed parameters.
531+ /// </summary>
532+ /// <param name="method">The target method being mocked.</param>
533+ /// <param name="handler">The handler action that receives a subset of parameters.</param>
534+ /// <param name="handlerParamTypes">The types of parameters expected by the handler.</param>
535+ private static void ValidateAndSetupOnCall ( MethodInfo method , Type [ ] handlerParamTypes , string parameterName )
536+ {
537+ var methodParams = method . GetParameters ( ) ;
538+
539+ // Validate that the method has at least as many parameters as the handler expects
540+ if ( methodParams . Length < handlerParamTypes . Length )
541+ {
542+ throw new ArgumentException (
543+ $ "The method '{ method . Name } ' has { methodParams . Length } parameter(s), but the handler expects { handlerParamTypes . Length } parameter(s). " +
544+ $ "The handler can only receive up to { methodParams . Length } parameter(s).",
545+ parameterName ) ;
546+ }
547+
548+ // Validate type compatibility for each handler parameter
549+ for ( int i = 0 ; i < handlerParamTypes . Length ; i ++ )
550+ {
551+ var methodParamType = methodParams [ i ] . ParameterType ;
552+ var handlerParamType = handlerParamTypes [ i ] ;
553+
554+ if ( ! handlerParamType . IsAssignableFrom ( methodParamType ) && methodParamType != handlerParamType )
555+ {
556+ throw new ArgumentException (
557+ $ "Parameter type mismatch at position { i } : method '{ method . Name } ' has parameter type '{ methodParamType . Name } ' " +
558+ $ "but handler expects '{ handlerParamType . Name } '.",
559+ parameterName ) ;
560+ }
561+ }
562+ }
563+
463564 // --- Callback Methods ---
464565
465566 /// <summary>
@@ -613,11 +714,8 @@ public Mock<T> OnCall(Expression<Func<T, object?>> expression, Action handler)
613714 public Mock < T > OnCall < T1 > ( Expression < Func < T , object ? > > expression , Action < T1 > handler )
614715 {
615716 var ( method , _) = ExtractMethod ( expression ) ;
616- _proxy . OnInvocation ( method , args =>
617- {
618- if ( args . Length >= 1 )
619- handler ( ( T1 ) args [ 0 ] ! ) ;
620- } ) ;
717+ ValidateAndSetupOnCall ( method , new [ ] { typeof ( T1 ) } , nameof ( handler ) ) ;
718+ _proxy . OnInvocation ( method , args => handler ( ( T1 ) args [ 0 ] ! ) ) ;
621719 return this ;
622720 }
623721
@@ -641,11 +739,8 @@ public Mock<T> OnCall<T1>(Expression<Func<T, object?>> expression, Action<T1> ha
641739 public Mock < T > OnCall < T1 , T2 > ( Expression < Func < T , object ? > > expression , Action < T1 , T2 > handler )
642740 {
643741 var ( method , _) = ExtractMethod ( expression ) ;
644- _proxy . OnInvocation ( method , args =>
645- {
646- if ( args . Length >= 2 )
647- handler ( ( T1 ) args [ 0 ] ! , ( T2 ) args [ 1 ] ! ) ;
648- } ) ;
742+ ValidateAndSetupOnCall ( method , new [ ] { typeof ( T1 ) , typeof ( T2 ) } , nameof ( handler ) ) ;
743+ _proxy . OnInvocation ( method , args => handler ( ( T1 ) args [ 0 ] ! , ( T2 ) args [ 1 ] ! ) ) ;
649744 return this ;
650745 }
651746
@@ -671,11 +766,8 @@ public Mock<T> OnCall<T1, T2>(Expression<Func<T, object?>> expression, Action<T1
671766 public Mock < T > OnCall < T1 , T2 , T3 > ( Expression < Func < T , object ? > > expression , Action < T1 , T2 , T3 > handler )
672767 {
673768 var ( method , _) = ExtractMethod ( expression ) ;
674- _proxy . OnInvocation ( method , args =>
675- {
676- if ( args . Length >= 3 )
677- handler ( ( T1 ) args [ 0 ] ! , ( T2 ) args [ 1 ] ! , ( T3 ) args [ 2 ] ! ) ;
678- } ) ;
769+ ValidateAndSetupOnCall ( method , new [ ] { typeof ( T1 ) , typeof ( T2 ) , typeof ( T3 ) } , nameof ( handler ) ) ;
770+ _proxy . OnInvocation ( method , args => handler ( ( T1 ) args [ 0 ] ! , ( T2 ) args [ 1 ] ! , ( T3 ) args [ 2 ] ! ) ) ;
679771 return this ;
680772 }
681773
@@ -720,11 +812,8 @@ public Mock<T> OnCall(Expression<Action<T>> expression, Action handler)
720812 public Mock < T > OnCall < T1 > ( Expression < Action < T > > expression , Action < T1 > handler )
721813 {
722814 var method = ExtractVoidMethod ( expression ) ;
723- _proxy . OnInvocation ( method , args =>
724- {
725- if ( args . Length >= 1 )
726- handler ( ( T1 ) args [ 0 ] ! ) ;
727- } ) ;
815+ ValidateAndSetupOnCall ( method , new [ ] { typeof ( T1 ) } , nameof ( handler ) ) ;
816+ _proxy . OnInvocation ( method , args => handler ( ( T1 ) args [ 0 ] ! ) ) ;
728817 return this ;
729818 }
730819
@@ -748,11 +837,8 @@ public Mock<T> OnCall<T1>(Expression<Action<T>> expression, Action<T1> handler)
748837 public Mock < T > OnCall < T1 , T2 > ( Expression < Action < T > > expression , Action < T1 , T2 > handler )
749838 {
750839 var method = ExtractVoidMethod ( expression ) ;
751- _proxy . OnInvocation ( method , args =>
752- {
753- if ( args . Length >= 2 )
754- handler ( ( T1 ) args [ 0 ] ! , ( T2 ) args [ 1 ] ! ) ;
755- } ) ;
840+ ValidateAndSetupOnCall ( method , new [ ] { typeof ( T1 ) , typeof ( T2 ) } , nameof ( handler ) ) ;
841+ _proxy . OnInvocation ( method , args => handler ( ( T1 ) args [ 0 ] ! , ( T2 ) args [ 1 ] ! ) ) ;
756842 return this ;
757843 }
758844
@@ -778,11 +864,8 @@ public Mock<T> OnCall<T1, T2>(Expression<Action<T>> expression, Action<T1, T2> h
778864 public Mock < T > OnCall < T1 , T2 , T3 > ( Expression < Action < T > > expression , Action < T1 , T2 , T3 > handler )
779865 {
780866 var method = ExtractVoidMethod ( expression ) ;
781- _proxy . OnInvocation ( method , args =>
782- {
783- if ( args . Length >= 3 )
784- handler ( ( T1 ) args [ 0 ] ! , ( T2 ) args [ 1 ] ! , ( T3 ) args [ 2 ] ! ) ;
785- } ) ;
867+ ValidateAndSetupOnCall ( method , new [ ] { typeof ( T1 ) , typeof ( T2 ) , typeof ( T3 ) } , nameof ( handler ) ) ;
868+ _proxy . OnInvocation ( method , args => handler ( ( T1 ) args [ 0 ] ! , ( T2 ) args [ 1 ] ! , ( T3 ) args [ 2 ] ! ) ) ;
786869 return this ;
787870 }
788871
0 commit comments