如何使用反射来获取泛型类型的扩展方法

发布于 2024-11-06 01:02:31 字数 3979 浏览 0 评论 0原文

从互联网上的各种来源中,我收集到了以下函数:

public static Nullable<T> TryParseNullable<T>(this Nullable<T> t, string input) where T : struct
{
    if (string.IsNullOrEmpty(input))
        return default(T);

    Nullable<T> result = new Nullable<T>();
    try
    {
        IConvertible convertibleString = (IConvertible)input;
        result = new Nullable<T>((T)convertibleString.ToType(typeof(T), CultureInfo.CurrentCulture));
    }
    catch (InvalidCastException) { }
    catch (FormatException) { }

    return result;
}

我已将其放入扩展方法中,如果我直接调用它,它就可以正常工作:

int? input = new int?().TryParseNullable("12345");

当我尝试使用上下文中的反射来调用它时,就会出现问题另一个通用函数。 SO 充满了描述如何获取泛型方法和静态方法的 MethodInfo 的答案,但我似乎无法以正确的方式将它们放在一起。
我已经正确确定传递的泛型类型本身就是泛型类型 (Nullable<>),现在我想使用反射来调用 TryParseNullable 扩展方法Nullable<>

public static T GetValue<T>(string name, T defaultValue)
{
    string result = getSomeStringValue(name);
    if (string.IsNullOrEmpty(result)) return defaultValue;

    try
    {
        if (typeof(T).IsGenericType && typeof(T).GetGenericTypeDefinition() == typeof(Nullable<>))
        {
            MethodInfo methodInfo;

            //using the TryParse() of the underlying type works but isn't exactly the way i want to do it
            //------------------------------------------------------------------------------------------- 
            NullableConverter nc = new NullableConverter(typeof(T));
            Type t = nc.UnderlyingType;

            methodInfo = t.GetMethod("TryParse", BindingFlags.Public | BindingFlags.Static, Type.DefaultBinder, new[] { typeof(string), t.MakeByRefType() }, null);
            if (methodInfo != null)
            {
                var inputParameters = new object[] { result, null };
                methodInfo.Invoke(null, inputParameters);
                return (T) inputParameters[1];
            }

            //start of the problem area
            //-------------------------

            Type ttype = typeof(T);

            //this works but is undesirable (due to reference to class containing the static method):
            methodInfo = typeof(ParentExtensionsClass).GetMethod("TryParseNullable", BindingFlags.Public | BindingFlags.Static);
            if (methodInfo != null)
                Console.WriteLine(methodInfo);

            //standard way of getting static method, doesn't work (GetMethod() returns null):
            methodInfo = ttype.GetMethod("TryParseNullable", BindingFlags.Public | BindingFlags.Static);
            if (methodInfo != null)
                Console.WriteLine(methodInfo);

            //Jon Skeet's advised method, doesn't work in this case (again GetMethod() returns null):
            //(see footnote for link to this answer)
            methodInfo = ttype.GetMethod("TryParseNullable");
            methodInfo = methodInfo.MakeGenericMethod(ttype);
            if (methodInfo != null)
                Console.WriteLine(methodInfo);

            //another random attempt (also doesn't work):
            methodInfo = ttype.GetMethod("TryParseNullable", BindingFlags.Public | BindingFlags.Static, Type.DefaultBinder, new[] { typeof(string) }, null);
            if (methodInfo != null)
                Console.WriteLine(methodInfo);
        }

        // if we get this far, then we are not handling the type yet
        throw new ArgumentException("The type " + defaultValue.GetType() + " is not yet supported by GetValue<T>.", "T");
    }
    catch (Exception e)
    {
        [snip]
    }
}

有人能让我摆脱痛苦吗?
typeof(T) 返回正确的类型信息,我想我可能在 GetMethod() 调用中使用它有点不正确,或者我没有指定通过调用 GetMethod() 获取正确的参数。

<子>1。 引用的 Jon Skeet 答案的链接

From various sources on teh interwebs I've gleaned this following function:

public static Nullable<T> TryParseNullable<T>(this Nullable<T> t, string input) where T : struct
{
    if (string.IsNullOrEmpty(input))
        return default(T);

    Nullable<T> result = new Nullable<T>();
    try
    {
        IConvertible convertibleString = (IConvertible)input;
        result = new Nullable<T>((T)convertibleString.ToType(typeof(T), CultureInfo.CurrentCulture));
    }
    catch (InvalidCastException) { }
    catch (FormatException) { }

    return result;
}

I've made it into an extension method, and it works just fine if I call it directly:

int? input = new int?().TryParseNullable("12345");

My problem occurs when I try to call it using reflection from within the context of another generic function. SO is full of answers describing how to get the MethodInfo of generic methods and static methods, but I can't seem to put these together in the right way.
I've correctly determined that the passed generic type is itself a generic type (Nullable<>), now I want to use reflection to call the TryParseNullable extension method on the Nullable<>:

public static T GetValue<T>(string name, T defaultValue)
{
    string result = getSomeStringValue(name);
    if (string.IsNullOrEmpty(result)) return defaultValue;

    try
    {
        if (typeof(T).IsGenericType && typeof(T).GetGenericTypeDefinition() == typeof(Nullable<>))
        {
            MethodInfo methodInfo;

            //using the TryParse() of the underlying type works but isn't exactly the way i want to do it
            //------------------------------------------------------------------------------------------- 
            NullableConverter nc = new NullableConverter(typeof(T));
            Type t = nc.UnderlyingType;

            methodInfo = t.GetMethod("TryParse", BindingFlags.Public | BindingFlags.Static, Type.DefaultBinder, new[] { typeof(string), t.MakeByRefType() }, null);
            if (methodInfo != null)
            {
                var inputParameters = new object[] { result, null };
                methodInfo.Invoke(null, inputParameters);
                return (T) inputParameters[1];
            }

            //start of the problem area
            //-------------------------

            Type ttype = typeof(T);

            //this works but is undesirable (due to reference to class containing the static method):
            methodInfo = typeof(ParentExtensionsClass).GetMethod("TryParseNullable", BindingFlags.Public | BindingFlags.Static);
            if (methodInfo != null)
                Console.WriteLine(methodInfo);

            //standard way of getting static method, doesn't work (GetMethod() returns null):
            methodInfo = ttype.GetMethod("TryParseNullable", BindingFlags.Public | BindingFlags.Static);
            if (methodInfo != null)
                Console.WriteLine(methodInfo);

            //Jon Skeet's advised method, doesn't work in this case (again GetMethod() returns null):
            //(see footnote for link to this answer)
            methodInfo = ttype.GetMethod("TryParseNullable");
            methodInfo = methodInfo.MakeGenericMethod(ttype);
            if (methodInfo != null)
                Console.WriteLine(methodInfo);

            //another random attempt (also doesn't work):
            methodInfo = ttype.GetMethod("TryParseNullable", BindingFlags.Public | BindingFlags.Static, Type.DefaultBinder, new[] { typeof(string) }, null);
            if (methodInfo != null)
                Console.WriteLine(methodInfo);
        }

        // if we get this far, then we are not handling the type yet
        throw new ArgumentException("The type " + defaultValue.GetType() + " is not yet supported by GetValue<T>.", "T");
    }
    catch (Exception e)
    {
        [snip]
    }
}

Can someone put me out of my misery?
The typeof(T) returns the correct type info, I figure that maybe I'm using it a little incorrectly with the GetMethod() call, or I haven't specified the right parameters with the call to GetMethod().

1. Link to referenced Jon Skeet answer

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

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

发布评论

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

评论(1

几味少女 2024-11-13 01:02:31

问题在于扩展方法不会修改它们“扩展”的类型。幕后实际发生的事情是,编译器透明地将所有似乎对相关对象进行的调用转换为对静态方法的调用。

IE。

int? input = new int?().TryParseNullable("12345");
// becomes...
int? input = YourClass.TryParseNullable(new int?(), "12345");

从那里开始,为什么它没有通过反射显示出来就变得很明显了。这也解释了为什么您必须为定义 YourClass 的命名空间使用 using 指令,以使扩展方法对编译器可见。至于如何实际获取这些信息,我不确定是否有一种方法,不需要运行所有声明的类型(也许是有趣的类的过滤列表,如果您在编译时知道此类信息)对于定义了 ExtensionMethodAttribute ([ExtensionMethod]) 的静态方法,然后尝试解析参数列表的 MethodInfo 来计算是否他们致力于可为空

The problem is that extension methods don't modify the type they are 'extending'. What actually happens behind the scenes is that the compiler transparently translates all the calls that seem to be made on the object in question to calls to your static method.

ie.

int? input = new int?().TryParseNullable("12345");
// becomes...
int? input = YourClass.TryParseNullable(new int?(), "12345");

From there it becomes obvious why it's not showing up via reflection. This also explains why you have to have a using directive for the namespace where YourClass is defined for the extension methods to be visible to the compiler. As to how you can actually get at that information, I'm not sure there is a way, short of running over all the declared types (perhaps a filtered list of interesting classes, if you know that sort of information at compile time) looking for static methods with the ExtensionMethodAttribute ([ExtensionMethod]) defined on them, then trying to parse the MethodInfo for the parameter list to work out if they work on Nullable<>.

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