Я пытаюсь создать Делегата к чтению/записи свойств неизвестного типа класса во времени выполнения.
У меня есть универсальный класс Main
и метод, который похож на это:
Delegate.CreateDelegate(typeof(Func), get)
где get
a MethodInfo
из свойства, которое должно быть считано. Проблема - это, когда свойство возвращается int
(Я предполагаю, что это происходит для типов значения), вышеупомянутый код бросает ArgumentException, потому что метод не может быть связан. В случае строки это работает хорошо.
Для решения проблемы, я изменил код так, чтобы соответствующий тип Делегата был сгенерирован при помощи MakeGenericType
. Таким образом, теперь код:
Type func = typeof(Func<,>);
Type generic = func.MakeGenericType(typeof(T), get.ReturnType);
var result = Delegate.CreateDelegate(generic, get)
Проблема теперь состоит в том что созданный экземпляр делегата generic
таким образом, я должен использовать DynamicInvoke
который был бы столь же медленным как использование чистого отражения для чтения поля.
Таким образом, мой вопрос состоит в том, почему это - первый отрывок сбоев кода с типами значения. Согласно MSDN это должно работать, поскольку это говорит это
Тип возврата делегата совместим с типом возврата метода, если тип возврата метода более строг, чем тип возврата делегата
и как выполнить делегата во втором отрывке так, чтобы это было быстрее, чем отражение.
Спасибо.
Вот один из способов решения вашей проблемы. Создайте общий метод:
public static Func<T, object> MakeDelegate<U>(MethodInfo @get)
{
var f = (Func<T, U>)Delegate.CreateDelegate(typeof(Func<T, U>), @get);
return t => f(t);
}
Таким образом, компилятор C# позаботится о вставке необходимых боксов (если таковые имеются) для преобразования f(t)
(типа U
) в object
. Теперь вы можете использовать отражение, чтобы вызвать этот метод MakeDelegate
с U
, установленным в @get.ReturnType
, и то, что вы получите обратно, будет Func
, который можно вызвать без необходимости прибегать к использованию DynamicInvoke
.
Вызов завершился неудачно, потому что вам нужен объект, а не тип значения (например, INT) - очевидно, Func
не является Func
- это не будет работать с любым vt, таким как double или bool. Либо верните упакованный Int (или все, что у вас есть). или (возможно, лучше) использовать API-интерфейс отражения.
Используя классы испускания отражения, Вы можете создавать динамические методы и сохранять их как делегаты, или создавать динамические делегаты и сохранять их в какой-то своей структуре. Вы можете сделать это только один раз (возможно, один раз за время выполнения), сохранить его в каком-либо Dict и вызывать при необходимости.
надеюсь, что это поможет. люк
Ваш исходный код может работать только для ссылочных типов. Вот почему строка не была проблемой, она напрямую происходит от System.Object. То, что тип значения является производным от ValueType и Object, является хорошей иллюзией на бумаге, но на самом деле требует кода. Компилятор C # автоматически генерирует этот код, для этого требуется преобразование упаковки. Это та часть, которая здесь отсутствует, нет преобразования времени выполнения из int в объект без кода операции BOX .
Вы можете получить этот код операции в своем коде, но вам придется использовать System.Reflection.Emit.
Прежде чем идти туда, сначала проверьте, не слишком ли медленно то, что у вас сейчас есть. Расходы на отражение - это извлечение метаданных из сборки. Это было сделано при создании делегата, после этого информация о типе кэшируется.
Можно ли ограничить общий метод только работой со ссылочными типами и создать другой, чтобы он работал только с типами значений, и решить, какие функции использовать соответственно?