Проблема с типом возврата делегата - PullRequest
2 голосов
/ 10 марта 2011

Имеется следующий код

public delegate object ParseHandler(string s);
public static ParseHandler GetParser(Type t)
{
    MethodInfo parse = t.GetMethod("Parse", BindingFlags.Static | BindingFlags.Public,
        null, new Type[] { typeof(string) }, null);

    if (parse != null)
        return (ParseHandler)Delegate.CreateDelegate(typeof(ParseHandler), parse, true);

    return null;
}

, вызывающий вызов Delegate.CreateDelegate() с сообщением «Ошибка привязки к целевому методу».очевидно, потому что ParseHandler определяется как возвращающее object вместо возвращаемого значения, специфичного для типа.

Я не знаю тип в типе компиляции, поэтому я не могу заменить параметр type на универсальный (иесли бы я это сделал, тогда вся функция была бы не нужна).

Немного запутался, как с этим бороться.Требуемое поведение - найти метод public static Parse(string) для данного типа и создать делегат для его последующего вызова.

1 Ответ

5 голосов
/ 10 марта 2011

Все должно быть в порядке, если реальное возвращаемое значение является ссылочным типом, но имеет смысл, что это не удастся для возвращаемого значения типа значения. (См. Сообщение Эрика Липперта о представлении и идентичности для более подробных объяснений.)

Один из вариантов - создать универсальный класс, подобный этому:

public class BoxingParserDelegate<T>
{
    private readonly Converter<string, T> parser;

    public BoxingParserDelegate(Converter<string, T> parser)
    {
        this.parser = parser;
    }

    public object Parse(string x)
    {
        return parser(x);
    }
}

В вашем методе GetParser вы проверяете, является ли тип возвращаемого значения parse типом значения, и, если это так, создаете экземпляр BoxingParserDelegate<T> с помощью отражения. Затем вы можете создать экземпляр ParseHandler из метода Parse BoxingParserDelegate.

Все это будет откровенно несколько неловко, но должно сработать. Чувствуется, что должен быть более простой подход, если честно.

(На .NET 3.5 или выше я бы рекомендовал использовать деревья выражений, но я только что видел тег .NET 2.0.)

РЕДАКТИРОВАТЬ: Ага, подумал о несколько более простой подход:

public static ParseHandler BuildParseHandler<T>(Converter<string, T> converter)
{
    // Performs boxing automatically
    return delegate(string x) { return converter(x); }
}

Это фактически , требующий, чтобы компилятор выполнил для вас половину вышеуказанной работы. Тогда вам просто нужно выяснить, нужно ли вам это называть, и, если необходимо, сделать это с отражением. Примерно так:

public static ParseHandler GetParser(Type t)
{
    MethodInfo parse = t.GetMethod("Parse",
        BindingFlags.Static | BindingFlags.Public,
        null, new Type[] { typeof(string) }, null);

    // Method not found
    if (parse == null)
    {
        return null;
    }

    // Reference type - use delegate covariance
    if (!parse.ReturnType.IsValueType)
    {
        return (ParseHandler) Delegate.CreateDelegate(typeof(ParseHandler),
            parse, true);
    }

    // Tricky situation: call BuildParseHandler with generics
    Type delegateType = typeof(Converter<,>).MakeGenericType(typeof(string),
                                                             parse.ReturnType);
    object converter = Delegate.CreateDelegate(delegateType, parse, true);

    // You may need extra work to get this... let me know whether or not it works.
    // Obviously if you make it private, you'll need extra binding flags.
    MethodInfo method = typeof(TypeContainingThisMethod)
                            .GetMethod("BuildParseHandler");
    method = method.MakeGenericMethod(parse.ReturnType);

    return (ParseHandler) method.Invoke(null, new object[] { converter });
}
...