CreateDelegate с неизвестными типами - PullRequest
11 голосов
/ 22 марта 2010

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

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

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

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

Спасибо.

Ответы [ 4 ]

10 голосов
/ 22 марта 2010

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

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<T, object>, который можно вызвать без необходимости прибегать к использованию DynamicInvoke.

3 голосов
/ 22 марта 2010

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

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

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

2 голосов
/ 22 марта 2010

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

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

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

0 голосов
/ 22 марта 2010

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

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