2010-03-22 2 views
11

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

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

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

Спасибо.

ответ

10

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

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.

+0

Большое спасибо, это сработало! – Giorgi

2

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

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

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

+0

Все типы значений наследуются от объекта, поэтому я решил, что это сработает. Я знаю, что излучение отражается, но это сложнее по сравнению с CreateDelegate – Giorgi

+0

, но ковариация и контравариантность НЕ работают с типами значений. Это все цели типов ценностей и бокса. http://msdn.microsoft.com/en-us/magazine/cc163727.aspx , потому что нет неявного более здесь http://blogs.msdn.com/ericlippert/archive/2007/10/ 19/covariance-and-contravariance-in-c-part-three-member-group-conversion-variance.aspx – luckyluke

3

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

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

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

+1

Итак, вы имеете в виду, что использование DynamicInvoke для делегата, сгенерированного в фрагменте кода, выполняется быстро или я ошибаюсь? В настоящее время я использую SetValue/GetValue для записи/чтения свойств и хотел бы сделать это быстрее. – Giorgi

0

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

Смежные вопросы