Как использовать отражение, чтобы получить метод расширения для универсального типа - PullRequest
10 голосов
/ 11 мая 2011

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

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

Может ли кто-нибудь избавить меня от моих страданий?
The typeof(T) возвращает правильную информацию о типе, я полагаю, что, возможно, я использую ее немного неправильно с вызовом GetMethod(), или я не указал правильные параметры с вызовом GetMethod().

1. Ссылка на указанный ответ Джона Скита

1 Ответ

7 голосов
/ 11 мая 2011

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

т.е..

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...