My suggestions, in my personally-recommended order:
Option 1: Assuming those primitives are often used together, wrap them all in an object that models their collective value to the problem domain, and perform the validation in that class. If you can, make it a struct, so you don't require null checks in both methods.
Option 2: Handle the validation in calling code that is in a higher tier, layer, or level of abstraction, assuming that reduces the duplication
Option 3: Using Aspects (aop).
Aspect-oriented programming specializes in handling cross-cutting concerns that object-oriented programming can sometimes struggle with, such as logging, validation, or error handling. It works through the use of 'Aspects' which are pieces of code that can be applied to multiple methods, classes, or even assemblies. For example, in your case, you might have a 'ParameterValidationAspect' that, when applied to a method, validates the method arguments and determines whether the method should be executed or if another value should be returned instead.
The code can vary greatly based on the implementation, but a common pattern is to have an attribute that is applied to a parameter, method, class, or assembly, similar to this sample:
public void Method1()
// no code in here, but aspect logs before method is entered and after method exits
public ResponseType Method2(string p1, int p2)
//ValidateParameters aspect will perform the validation and return the appropriate response.
return new ResponseType();
The exact mechanism aspect-oriented programming uses to insert logic into methods can vary, based on the implementation you go with. Generally, there's three mechanisms an aop library uses :
- Inserting code after compilation by modifying the byte or ilsm code (C#'s PostSharp uses this).
- Intercepting method calls with a wrapper object.
- Using reflection to modify the class at runtime (can overlap a bit with #2)
Some implementations might provide hooks for you to tap into, like 'PreMethodExecute', 'MethodExecution', 'OnException', etc.
I enjoy the concept of Aop, but its complexity can be substantial, especially in the beginning of the learning curve. It's difficult for a single developer to introduce it into a system, and pursuing this type of solution should have buy-in across your teams.