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

标签 c# generics reflection extension-methods

从互联网上的各种来源,我收集了以下功能:

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。 Link to referenced Jon Skeet answer

最佳答案

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

即。

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

从那里很明显为什么它没有通过反射出现。这也解释了为什么你必须有一个 using YourClass 所在命名空间的指令为编译器可见的扩展方法定义。至于你如何真正获得这些信息,我不确定是否有一种方法,除了遍历所有声明的类型(如果你在编译时知道那种信息,可能是有趣类的过滤列表)看起来对于带有 ExtensionMethodAttribute 的静态方法( [ExtensionMethod] ) 在它们上定义,然后尝试解析 MethodInfo如果参数列表在 Nullable<> 上工作,则参数列表会计算出来.

关于c# - 如何使用反射获取泛型类型的扩展方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5959219/

相关文章:

java - 具有匿名类的 CGLib 增强器

c# - 将 WrapPanel 虚拟化为 ListView 的 ItemsTemplate

c# - 如何在 F# 或 C# 中将像素绘制到位图上而不是抗锯齿点上?

c# - 自动将 JWT 加载到 User.Identity (ASP.NET Core 3.1)

java - List<Dog> 是 List<Animal> 的子类吗?为什么 Java 泛型不是隐式多态的?

c# - 反射与编译以获得 MemberExpression 的值(value)

c# - 我可以使用 BouncycaSTLe for C# 使用密码创建可重现的 PGP key 对吗?

c# - 当一个类没有实现必需的属性时,是否会抛出标准的 .NET 异常?

java - 为什么这个通用的 java 代码不能编译?

java - 如何实例化具有带参数的私有(private)构造函数的泛型类