Reputation: 9966
Consider the following extension method:
public static class ValiDoc
{
public static IEnumerable<RuleDescription> GetRules<T>(this AbstractValidator<T> validator, bool documentNested = false)
{
//....
}
}
An implementation of AbstractValidator:
public class AddressValidator : AbstractValidator<Address>
{
public AddressValidator()
{
RuleFor(address => address.HouseNumber).NotEmpty();
RuleFor(address => address.StreetName).NotEmpty();
RuleFor(address => address.PostCode).NotEmpty();
}
}
I want to invoke the GetRules() extension method on ValiDoc via reflection and pass in an instance of AddressValidator as the first parameter, with a boolean for the second.
Having never played with Reflection before I am pretty overwhelmed, however by following the examples available here I have made some progress.
//Parameter 1
Type type = typeof(AbstractValidator<>);
// Instance of Address
Type constructed = type.MakeGenericType(childValidator.ValidatorType.GetTypeInfo().BaseType.GenericTypeArguments[0]);
// Find the extension method based on the signature I have defined for the usage
// public static IEnumerable<RuleDescription> GetRules<T>(this AbstractValidator<T> validator, bool documentNested = false)
var runtimeMethods = typeof(ValiDoc).GetRuntimeMethods();
MethodInfo generatedGetRules = null;
// Nothing fancy for now, just pick the first option as we know it is GetRules
using (IEnumerator<MethodInfo> enumer = runtimeMethods.GetEnumerator())
{
if (enumer.MoveNext()) generatedGetRules = enumer.Current;
}
// Create the generic method instance of GetRules()
generatedGetRules = generatedGetRules.MakeGenericMethod(constructed);
//Parameter 1 = Derived from AbstractValidator<T>, Parameter 2 = boolean
var parameterArray = new object[] { childValidator.GetValidator(new PropertyValidatorContext(new ValidationContext(rule.Member.DeclaringType), rule, propertyName)), true };
//Invoke extension method with validator instance
generatedGetRules.Invoke(null, parameterArray);
When executing generatedGetRules.Invoke, I receive the following error message:
'Object of type 'ValiDoc.Tests.TestData.Validators.AddressValidator' cannot be converted to type 'FluentValidation.AbstractValidator
1[FluentValidation.AbstractValidator
1[ValiDoc.Tests.TestData.POCOs.Address]]'.'
Have I missed anything obvious? I can't tell if I am nearly there or miles away.
Supporting values:
typeof(AbstractValidator<>) = {FluentValidation.AbstractValidator`1[T]}
childValidator.ValidatorType.GetTypeInfo().BaseType.GenericTypeArguments[0] = {ValiDoc.Tests.TestData.POCOs.Address}
constructed = {FluentValidation.AbstractValidator`1[ValiDoc.Tests.TestData.POCOs.Address]}
generatedGetRules = {System.Collections.Generic.IEnumerable1[ValiDoc.Output.RuleDescription] GetRules[AbstractValidator
1](FluentValidation.AbstractValidator1[FluentValidation.AbstractValidator
1[ValiDoc.Tests.TestData.POCOs.Address]], Boolean)}
Parameter array = {ValiDoc.Tests.TestData.Validators.AddressValidator}, boolean
EDIT: Working implementation:
// Find the extension method based on the signature I have defined for the usage
// public static IEnumerable<RuleDescription> GetRules<T>(this AbstractValidator<T> validator, bool documentNested = false)
var runtimeMethods = typeof(ValiDoc).GetRuntimeMethods();
MethodInfo generatedGetRules = null;
// Nothing fancy for now, just pick the first option as we know it is GetRules
using (IEnumerator<MethodInfo> enumer = runtimeMethods.GetEnumerator())
{
if (enumer.MoveNext()) generatedGetRules = enumer.Current;
}
// Create the generic method instance of GetRules()
generatedGetRules = generatedGetRules.MakeGenericMethod(childValidator.ValidatorType.GetTypeInfo().BaseType.GenericTypeArguments[0]);
//Parameter 1 = Derived from AbstractValidator<T>, Parameter 2 = boolean
var parameterArray = new object[] { childValidator.GetValidator(new PropertyValidatorContext(new ValidationContext(rule.Member.DeclaringType), rule, propertyName)), true };
//Invoke extension method with validator instance
var output = generatedGetRules.Invoke(null, parameterArray) as IEnumerable<RuleDescription>;
Upvotes: 1
Views: 300
Reputation: 49179
In your error message, I see AddressValidator
(AbstractValidator<Address>
) can't be converted to AbstractValidator<AbstractValidator<Address>>
, which is exactly what you're doing. Constructed is not what you want to pass into MakeGenericMethod, I'm assuming, since the method is generic on T, but the first argument is this AbstractValidator<T>
so you're getting a double generic specialization instead of what you want.
Upvotes: 1