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

CreateDelegate с неизвестными типами

Я пытаюсь создать делегат для чтения/записи свойств неизвестного типа класса во время выполнения.

У меня есть общий класс Main<T> и метод, который выглядит так:

Delegate.CreateDelegate(typeof(Func<T, object>), get)

где get является 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, он должен работать, поскольку он говорит, что

Тип возврата делегата совместим с типом возвращаемого метода, если возвращаемый тип метода более ограничительный, чем тип возвращаемого значения делегата

и как выполнить делегат во втором фрагменте, чтобы он был быстрее, чем отражение.

Спасибо.

4b9b3361

Ответ 1

Вот один из способов решения вашей проблемы. Создайте общий метод:

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);
}

Таким образом, компилятор С# позаботится о вставке необходимого бокса (если есть) для преобразования f(t) (типа U) в object. Теперь вы можете использовать отражение для вызова этого метода MakeDelegate с U, установленным на @get.ReturnType, и то, что вы получите, будет Func<T, object>, которое можно вызвать без необходимости прибегать к использованию DynamicInvoke.

Ответ 2

Исходный код может работать только для ссылочных типов. Поэтому строка не была проблемой, она напрямую вытекает из System.Object. То, что тип значения происходит из ValueType, а Object - хорошая иллюзия на бумаге, но на самом деле требует кода. Компилятор С# автоматически испускает этот код, для этого требуется преобразование в бокс. То, что здесь отсутствует, не существует преобразования времени выполнения из int в объект без код операции BOX.

Вы можете получить этот код операции в своем коде, но вам придется использовать System.Reflection.Emit.

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

Ответ 3

Вы вызываете сбои, потому что вам нужен объект, а не тип значения (например, INT) - очевидно, Func<T, int> не является Func<T, int> - он не будет работать ни с каким vt, как double или bool. Либо верните коробку Int (или что-нибудь, что у вас есть). или (возможно, лучше) использовать отражение emit API.

Используя классы отражения, вы можете создавать динамические методы и сохранять их в качестве делегатов или создавать динамические делегаты и сохранять их в некоторых из вашей структуры. Вы можете сделать это только один раз (возможно, один раз за время исполнения) хранить его в некоторых Dict и вызывать при необходимости.

надеюсь, что это поможет. Люк

Ответ 4

Можно ли ограничить общий метод только работой со ссылочными типами и создать другой, который будет работать только со значениями типов, и решить, какие функциональные возможности использовать соответственно?