Как сделать автоматическое преобразование типов для параметров при вызове метода с использованием отражения в C #? - PullRequest
8 голосов
/ 10 июля 2011

Мне нужно вызывать методы для типа через отражение с использованием C #.

Во время выполнения мои данные будут состоять из словаря, содержащего пары имя / значение.Имена в Словаре будут соответствовать именам параметров в методе, который я буду вызывать.Кроме того, во время выполнения у меня будет произвольное имя с указанием типа сборки и имя метода.Во время разработки у меня не будет никаких сведений о типе и методе, кроме того, что метод будет принимать переменное число параметров типа int, string, DateTime, bool, int [], string [], DateTime [] или bool[].

У меня нет проблем с тем, чтобы я смог создать экземпляр типа, используя отражение, и вызвать метод.Я застрял в точке, где мне нужно преобразовать строковые значения в моем словаре в соответствующий тип, необходимый для метода, когда я вызываю:

someMethodInfo.Invoke(instance, new [] { ... })

Я знаю, что мне нужно, вероятно, перечислить через MethodInfo.GetParameters() и выполните преобразование типа для каждого параметра.Я пытаюсь понять, как это сделать, и в идеале, как это сделать эффективно.

До сих пор мое исследование включало копание в исходный код MVC, так как оно делает нечто подобное при передаче значений формы вActionMethod.Я нашел ActionMethodDispatcher , но он использует выражения LINQ, с которыми я незнаком.

Я также смотрел похожие вопросы по SO, но не нашел ничего, что отвечало бы на мой вопрос.1015 * Буду рад любым указателям на решение.

Ответы [ 3 ]

4 голосов
/ 11 июля 2011

Вот код, который можно использовать для преобразования параметров:

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, лучше реализовать собственные конвертеры для каждого отдельного типа.1007 * Vitaliy

4 голосов
/ 11 июля 2011

Значение, которое вы хотите преобразовать, должно быть объектом, иначе преобразования вне стандартных типов не будут работать.Вы можете легко конвертировать между типами, например, так:

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);
}
2 голосов
/ 10 июля 2011

Возможно, хороший способ управлять "конвертерами" - это поддерживать Dictionary<Type, IMyTypeConverter> - где IMyTypeConverter имеет object Convert(string value).

...