2010-03-22 13 views
11

Próbuję utworzyć Delegata do odczytu/zapisu właściwości nieznanego typu klasy w czasie wykonywania.CreateDelegate przy nieznanych typach

mam rodzajowe klasy Main<T> oraz metodę, która wygląda tak:

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

gdzie get jest MethodInfo nieruchomości, które należy przeczytać. Problem polega na tym, że gdy właściwość zwraca int (domyślam się, że dzieje się to w przypadku typów wartości), powyższy kod generuje ArgumentException, ponieważ metoda nie może być powiązana. W przypadku sznurka działa dobrze.

Aby rozwiązać problem, zmieniłem kod tak, aby odpowiedni typ Delegata był generowany przy użyciu MakeGenericType. Więc teraz kod jest:

Type func = typeof(Func<,>); 
Type generic = func.MakeGenericType(typeof(T), get.ReturnType); 
var result = Delegate.CreateDelegate(generic, get) 

Problem jest teraz, że stworzony przykład delegat generic więc muszę używać DynamicInvoke, który byłby tak wolno, jak przy użyciu czystego odbicia do zapoznania się z boiska.

Moje pytanie brzmi: dlaczego pierwszy fragment kodu zawodzi z typami wartości. Według MSDN powinno działać jak mówi, że

Rodzaj powrót delegata jest zgodny z typem zwracanej przez metodę, jeśli typ zwracany tej metody jest bardziej restrykcyjne niż zwracany typ delegata

i jak wykonać delegata w drugim fragmencie, aby był szybszy od odbicia.

Dzięki.

Odpowiedz

10

Oto jeden ze sposobów rozwiązania problemu. Utworzyć metody rodzajowe:

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

ten sposób, C# 's kompilator dba o włożenie niezbędnej boks (jeśli w ogóle) do konwersji f(t) (typu U) do object. Teraz możesz użyć odbicia, aby wywołać tę metodę MakeDelegate z U ustawioną na @get.ReturnType, a otrzymasz odpowiedź Func<T, object>, którą można wywołać bez potrzeby używania numeru DynamicInvoke.

+0

Dziękuję bardzo, zadziałało! – Giorgi

2

Wywołanie nie powiedzie się, ponieważ potrzebujesz obiektu, a nie typu wartości (np. INT) - oczywiście Func<T, int> nie jest Func<T, Int> - nie będzie działał z żadnym vt jak podwójne lub bool. Zwróć pudełkowe Int (lub cokolwiek, co masz). lub (być może lepiej) użyj interfejsu API emitującego odbicia.

Korzystanie z klas emisji refleksów Można tworzyć metody dynamiczne i zapisywać je jako delegatów lub tworzyć dynamiczne delegaty i zapisywać je w niektórych strukturach. Możesz to zrobić tylko raz (być może raz na czas), zapisać go w jakimś Dict i wywołać w razie potrzeby.

nadzieję, że to pomaga. lette

+0

Wszystkie typy wartości dziedziczą z obiektu, dlatego pomyślałem, że to zadziała. Jestem świadomy emisji odbicia, ale jest to bardziej skomplikowane w porównaniu do CreateDelegate – Giorgi

+0

, ale kowariancja i contravariance NIE DZIAŁA z typami wartości. Taki jest cel typów wartości i boksowania. http://msdn.microsoft.com/en-us/magazine/cc163727.aspx ponieważ nie ma niejawna konwersja więcej tutaj http://blogs.msdn.com/ericlippert/archive/2007/10/ 19/kowariancja-i-wariancyjność-w-c-część-trzy-członek-grupa-konwersja-wariancja.aspx – luckyluke

3

Twój oryginalny kod może działać tylko dla typów referencji. Dlatego ciąg nie był problemem, pochodzi bezpośrednio z System.Object. To, że typ wartości wywodzi się z ValueType, a Obiekt jest miłym złudzeniem na papierze, ale w rzeczywistości wymaga kodu. Kompilator C# automatycznie emituje ten kod, wymaga konwersji bokserskiej. To jest ta część, której tutaj nie ma, nie ma konwersji środowiska wykonawczego z int na obiekt bez BOX opcode.

Możesz dostać ten kod operacyjny w swoim kodzie, ale będziesz musiał użyć System.Reflection.Emit.

Zanim tam pójdziesz, najpierw sprawdź, czy to, co masz teraz, jest zbyt wolne. Kosztem refleksji jest wykopywanie metadanych ze zgromadzenia. Stało się tak, gdy utworzyłeś delegata, informacje o typie są buforowane po tym.

+1

Masz na myśli to, że użycie DynamicInvoke na delegacie wygenerowanym w drugim fragmencie jest szybkie lub czy się mylę? Obecnie używam SetValue/GetValue do zapisywania/odczytu właściwości i chciałem, aby było to szybsze. – Giorgi

0

Czy możliwe jest ograniczenie ogólnej metody do pracy tylko z typami referencyjnymi i utworzenie innej tylko do pracy z typami wartości i podjęcie decyzji, której funkcji należy użyć odpowiednio?

Powiązane problemy