Подтвердить что ты не робот

Как использовать отражение для получения метода расширения на родовом типе

Из разных источников в interwebs я почерпнул эту следующую функцию:

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

4b9b3361

Ответ 1

Проблема заключается в том, что методы расширения не изменяют тип, который они "расширяют". То, что на самом деле происходит за кулисами, заключается в том, что компилятор прозрачно переводит все вызовы, которые, как представляется, сделаны на рассматриваемый объект, вызывает вызовы вашего статического метода.

т.

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

Отсюда становится очевидным, почему он не появляется через отражение. Это также объясняет, почему вы должны иметь директиву using для пространства имен, где YourClass определен для методов расширения, которые должны быть видимыми компилятору. Что касается того, как вы действительно можете получить эту информацию, я не уверен, что есть способ, не выполняющий все объявленные типы (возможно, отфильтрованный список интересных классов, если вы знаете такую ​​информацию во время компиляции) для статических методов с ExtensionMethodAttribute ([ExtensionMethod]), определенных на них, затем пытается проанализировать MethodInfo для списка параметров, если они работают на Nullable<>.