C#中使用反射调用方法时如何对参数进行自动类型转换?

发布于 2024-11-19 10:09:16 字数 739 浏览 9 评论 0原文

我需要使用 C# 通过反射调用类型上的方法。

在运行时,我的数据将由包含名称/值对的字典组成。字典中的名称将与我将调用的方法上的参数名称相对应。另外,在运行时,我将拥有任意程序集限定类型名称和方法名称。在设计时,我对类型和方法一无所知,除了该方法将接受可变数量的 int、string、DateTime、bool、int[]、string[]、DateTime[] 或 bool 类型的参数[]。

我可以毫无问题地使用反射创建该类型的实例并调用该方法。当我调用时,我陷入了必须将字典中的字符串值转换为方法所需的适当类型的境地:

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

我知道我可能需要通过 MethodInfo.GetParameters() 进行枚举并为每个值执行类型转换范围。我想要弄清楚的是如何做到这一点,理想情况下,如何有效地做到这一点。

到目前为止,我的研究涉及深入研究 MVC 源代码,因为它在将表单值传递给 ActionMethod 时执行类似的操作。我发现 ActionMethodDispatcher 但它使用我不熟悉的 LINQ 表达式。

我也在 SO 上查看了类似的问题,但没有找到任何可以回答我的问题的内容。

我欢迎任何有关解决方案的指示。

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

好久不见√ 2024-11-26 10:09:16

下面是一些可用于参数转换的代码:

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());
}

它支持基本类型、可空值和基本类型数组。
如果您要使用不支持 IConvertible 接口的类型 - 最好为每个单独的类型实现自定义转换器。

可以使用 Linq 以更优雅的方式编写它。

维塔利

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

叹梦 2024-11-26 10:09:16

要转换的值应该是一个对象,否则标准类型之外的转换将不起作用。您可以轻松地在类型之间进行转换,如下所示:

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);
}

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);
}
爱的十字路口 2024-11-26 10:09:16

也许管理“转换器”的一个好方法是维护一个 Dictionary - 其中 IMyTypeConverter 有一个 object Convert(string value)

Perhaps a nice way to manage "converters" is to maintain a Dictionary<Type, IMyTypeConverter> - where IMyTypeConverter has a object Convert(string value).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文