I have a need to invoke methods on a type through reflection using C#.
At run-time, my data will consist of a Dictionary containing name/value pairs. The names in the Dictionary will correspond to parameter names on the method I will invoke. Also, at run-time, I will have an arbitrary assembly qualified type name and a method name. At design time, I will have no knowledge of the type and the method other than that the method will accept a variable number of parameters of type int, string, DateTime, bool, int[], string[], DateTime[] or bool[].
I have no problem getting to the point where I can create an instance of the type using reflection and invoke the method. I am stuck at the point where I have to convert the string values in my dictionary to the appropriate type needed by the method when I call:
someMethodInfo.Invoke(instance, new [] { ... })
I know that I need to probably enumerate through MethodInfo.GetParameters() and perform the type conversion for each parameter. What I am trying to figure out is how to do this, and ideally, how to do it efficiently.
My research so far has involved digging into the MVC source code as it does something similar when passing form values to an ActionMethod. I found ActionMethodDispatcher but it uses LINQ Expressions, with which I am unfamiliar.
I also looked at similar questions on SO, but did not find anything that answers my question.
I would welcome any pointers to a solution.
Here is some code which can be used for parameters conversion:
public object ConvertSingleItem(string value, Type newType)
{
if (typeof(IConvertible).IsAssignableFrom(newType))
{
return Convert.ChangeType(value, newType);
}
else
{
// TODO: Add custom conversion for non IConvertible types
var converter = CustomConvertersFactory.GetConverter(newType);
return converter.Convert(value);
}
}
public object ConvertStringToNewNonNullableType(string value, Type newType)
{
// Do conversion form string to array - not sure how array will be stored in string
if (newType.IsArray)
{
// For comma separated list
Type singleItemType = newType.GetElementType();
var elements = new ArrayList();
foreach (var element in value.Split(','))
{
var convertedSingleItem = ConvertSingleItem(element, singleItemType);
elements.Add(convertedSingleItem);
}
return elements.ToArray(singleItemType);
}
return ConvertSingleItem(value, newType);
}
public object ConvertStringToNewType(string value, Type newType)
{
// If it's not a nullable type, just pass through the parameters to Convert.ChangeType
if (newType.IsGenericType && newType.GetGenericTypeDefinition().Equals(typeof(Nullable<>)))
{
if (value == null)
{
return null;
}
return ConvertStringToNewNonNullableType(value, new NullableConverter(newType).UnderlyingType);
}
return ConvertStringToNewNonNullableType(value, newType);
}
public object CallMethod(object instance, MethodInfo methodInfo, Dictionary<string, string> parameters)
{
var methodParameters = methodInfo.GetParameters();
var parametersForInvocation = new List<object>();
foreach (var methodParameter in methodParameters)
{
string value;
if (parameters.TryGetValue(methodParameter.Name, out value))
{
var convertedValue = ConvertStringToNewType(value, methodParameter.ParameterType);
parametersForInvocation.Add(convertedValue);
}
else
{
// Get default value of the appropriate type or throw an exception
var defaultValue = Activator.CreateInstance(methodParameter.ParameterType);
parametersForInvocation.Add(defaultValue);
}
}
return methodInfo.Invoke(instance, parametersForInvocation.ToArray());
}
It supports Primitive types, Nullables and Arrays of primitive types. In the case when you going to use types which doesn't support IConvertible interface - it is better to implement custom converters for each individual type.
It can be written in more elegant way with Linq.
Vitaliy
The value you want to convert should be an object, otherwise conversions outside the standard types will not work. You can easily convert between types like so:
object value = false; // false
Type chType = typeof(String); // System.String
object newValue = Convert.ChangeType(value, chType); // "false"
It's as easy as that. If you want a method:
public object ConvertType(object value, Type conversionType)
{
//Check if type is Nullable
if (conversionType.IsGenericType &&
conversionType.GetGenericTypeDefinition() == typeof(Nullable<>))
{
//If the type is Nullable and the value is null
//Just return null
if (value == null)
{
return null;
}
//Type is Nullable and we have a value, override conversion type to underlying
//type for the Nullable to avoid exception in Convert.ChangeType
var nullableConverter = new NullableConverter(conversionType);
conversionType = nullableConverter.UnderlyingType;
}
return Convert.ChangeType(value, conversionType);
}
Perhaps a nice way to manage "converters" is to maintain a Dictionary<Type, IMyTypeConverter>
- where IMyTypeConverter
has a object Convert(string value)
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With