Как сделать автоматическое преобразование типов для параметров при вызове метода с использованием отражения в C#?
У меня есть необходимость вызывать методы для типа через отражение с помощью C#.
Во время выполнения мои данные будут состоять из словаря, содержащего пары имя / значение. Имена в Словаре будут соответствовать именам параметров в методе, который я буду вызывать. Кроме того, во время выполнения у меня будет произвольное имя, полное имя сборки и имя метода. Во время разработки у меня не будет никаких сведений о типе и методе, кроме того, что метод будет принимать переменное число параметров типа int, string, DateTime, bool, int[], string[], DateTime[] или bool[].
У меня нет проблем, чтобы добраться до точки, где я могу создать экземпляр типа, используя отражение и вызвать метод. Я застрял в точке, где мне нужно преобразовать строковые значения в моем словаре в соответствующий тип, необходимый методу при вызове:
someMethodInfo.Invoke(instance, new [] { ... })
Я знаю, что мне нужно, вероятно, перечислить через MethodInfo.GetParameters() и выполнить преобразование типа для каждого параметра. Я пытаюсь понять, как это сделать, и в идеале, как это сделать эффективно.
До сих пор мое исследование включало копание в исходный код MVC, поскольку он делает нечто подобное при передаче значений формы в ActionMethod. Я нашел ActionMethodDispatcher, но он использует выражения LINQ, с которыми я незнаком.
Я также посмотрел на похожие вопросы по SO, но не нашел ничего, что отвечало бы на мой вопрос.
Я приветствовал бы любые указатели на решение.
3 ответа
Вот код, который можно использовать для преобразования параметров:
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());
}
Он поддерживает примитивные типы, Nullables и массивы примитивных типов. В случае, когда вы собираетесь использовать типы, которые не поддерживают интерфейс IConvertible, лучше реализовать собственные конвертеры для каждого отдельного типа.
Это может быть написано более элегантным способом с Linq.
Виталий
Значение, которое вы хотите преобразовать, должно быть объектом, иначе преобразования вне стандартных типов не будут работать. Вы можете легко конвертировать между типами, например, так:
object value = false; // false
Type chType = typeof(String); // System.String
object newValue = Convert.ChangeType(value, chType); // "false"
Это так просто. Если вы хотите метод:
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);
}
Возможно, хороший способ управлять "конвертерами" - это поддерживать Dictionary<Type, IMyTypeConverter>
- где IMyTypeConverter
имеет object Convert(string value)
,