Можете ли вы создать делегат метода экземпляра без указания экземпляра во время создания? Другими словами, можно ли создать «статический» делегат, который принимает в качестве первого параметра экземпляр, на который должен быть вызван метод?«Uncurrying» метод экземпляра в .NET
Например, как я могу построить следующий делегат с использованием отражения?
Func<int, string> = i=>i.ToString();
Я знаю о том, что я могу использовать methodInfo.Invoke, но это медленнее, и не проверяет тип корректности, пока не называется.
Если у вас есть MethodInfo
конкретного статического метода, можно построить делегат с помощью Delegate.CreateDelegate(delegateType, methodInfo)
, и все параметры статического метода остаются свободными.
Как указал Джон Скит, вы можете просто применить то же самое, чтобы сделать открытый делегат метода экземпляра, если этот метод не является виртуальным в ссылочном типе. Решать, какой метод вызывать по виртуальному методу сложно, так что это не так тривиально, а типы значений выглядят так, как будто они вообще не работают.
Для типов значений, CreateDelegate
демонстрирует очень странное поведение:
var func37 = (Func<CultureInfo,string>)(37.ToString);
var toStringMethod = typeof(int).GetMethod("ToString", BindingFlags.Instance | BindingFlags.Public, null, new Type[] {typeof(CultureInfo) }, null);
var func42 = (Func<CultureInfo,string>)Delegate.CreateDelegate(typeof(Func<CultureInfo,string>), 42, toStringMethod,true);
Console.WriteLine(object.ReferenceEquals(func37.Method,func42.Method)); //true
Console.WriteLine(func37.Target);//37
Console.WriteLine(func42.Target);//42
Console.WriteLine(func37(CultureInfo.InvariantCulture));//37
Console.WriteLine(func42(CultureInfo.InvariantCulture));//-201040128... WTF?
Вызова CreateDelegate
с null
, как целевой объект бросает связывания исключения, если метод экземпляра принадлежал к типу значения (это работает для ссылочных типов).
Некоторые последующие годы спустя: Неправильно переплете цель, которая вызвана func42(CultureInfo.InvariantCulture);
вернуть "-201040128"
вместо "42"
в моем примере, повреждение памяти, которая могла бы позволить удаленное выполнение кода (cve-2010-1898); это было исправлено в 2010 году в обновлении безопасности ms10-060. Текущие рамки корректно распечатывают 42! Это не облегчает ответ на этот вопрос, но объясняет особенно странное поведение в этом примере.
Это один из тех случаев, когда становится ясно, что C# все еще имеет место для роста в качестве функционального языка. Обработка функций как граждан первого класса по-прежнему остается не такой простой, как нам бы хотелось. Есть ли способ использовать динамические возможности на C# 4, чтобы упростить эту вещь? – LBushkin
@LBushkin: Я так не думаю. На самом деле динамическая типизация и lambdas не идут очень хорошо вместе для начала - компилятор должен знать, какой тип преобразовать лямбда-выражение во время компиляции. –
Я * тестировал на int.ToString, но для фактического использования я полагаю, что мог обойтись без виртуальных методов - хотя и не без структур. Во всяком случае, спасибо за хедз-ап, я упустил сложность виртуальных методов, и сообщение об ошибке не совсем информативно ... –