How to use reflection to get extension method on g

2020-08-16 07:45发布

问题:

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

回答1:

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<>.