2010-03-22 10 views
11

Sto provando a creare Delegare per leggere/scrivere proprietà di tipo sconosciuto di classe in fase di esecuzione.CreateDelegate con tipi sconosciuti

ho una classe generica Main<T> e un metodo che assomiglia a questo:

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

dove get è un MethodInfo della proprietà che dovrebbe essere letto. Il problema è che quando la proprietà restituisce int (suppongo che questo accada per i tipi di valore) il codice precedente genera ArgumentException perché il metodo non può essere associato. In caso di stringa funziona bene.

Per risolvere il problema, ho modificato il codice in modo che il tipo di delegato corrispondente venga generato utilizzando MakeGenericType. Così ora il codice è:

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

Il problema ora è che l'istanza delegato creato di generic quindi devo usare DynamicInvoke che sarebbe il più lentamente utilizzando la riflessione pura per leggere il campo.

Quindi la mia domanda è perché il primo frammento di codice non funziona con i tipi di valore. Secondo MSDN dovrebbe funzionare come si dice che

Il tipo di ritorno di un delegato è compatibile con il tipo di ritorno di un metodo se il tipo di ritorno del metodo è più restrittivo il tipo di ritorno del delegato

e come eseguire il delegato nel secondo snippet in modo che sia più veloce del riflesso.

Grazie.

risposta

10

Ecco un modo per risolvere il problema. Creare un metodo generico:

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

In questo modo, il compilatore C# 's si prende cura di inserire la boxe necessario (se presente) per convertire f(t) (di tipo U) per object. Ora puoi usare reflection per chiamare questo metodo MakeDelegate con U impostato su @get.ReturnType e quello che ottieni sarà un Func<T, object> che può essere chiamato senza bisogno di ricorrere all'utilizzo di DynamicInvoke.

+0

Grazie mille, ha funzionato! – Giorgi

2

L'invocazione non riesce perché l'oggetto non è un tipo di valore (come INT) - ovviamente Func<T, int> non è un Func<T, Int> - non funzionerà con alcun vt come double o bool. O restituisci un Int inscatolato (o qualunque cosa tu abbia). o (forse meglio) utilizzare la reflection emit API.

Utilizzando la riflessione emette le classi È possibile creare metodi dinamici e salvarli come delegati oppure creare delegati dinamici e salvarli in alcune delle strutture. Puoi farlo solo una volta (forse una volta per runtime) memorizzarlo in qualche Dict e richiamarlo quando necessario.

spero che sia d'aiuto. luke

+0

Tutti i tipi di valore ereditano dall'oggetto, ecco perché ho pensato che avrebbe funzionato. Sono consapevole del riflesso emesso ma è più complicato rispetto a CreateDelegate – Giorgi

+0

ma covarianza e controvarianza NON funziona con i tipi di valore. Questo è tutto lo scopo dei tipi di valore e della boxe. http://msdn.microsoft.com/en-us/magazine/cc163727.aspx perché non c'è nessuna conversione IMPLICIT altro qui http://blogs.msdn.com/ericlippert/archive/2007/10/ 19/covarianza-e-contrravarianza-in-c-parte-tre-membro-gruppo-conversione-varianza.aspx – luckyluke

3

Il codice originale può funzionare solo per i tipi di riferimento. Ecco perché la stringa non era un problema, deriva direttamente da System.Object. Che un valore deriva da ValueType e Object è una bella illusione su carta, ma in realtà richiede un codice. Il compilatore C# emette automaticamente quel codice, richiede una conversione di boxe. Questa è la parte che manca qui, non c'è conversione di runtime da int a oggetto senza lo BOX opcode.

È possibile ottenere questo codice operativo nel codice, ma sarà necessario utilizzare System.Reflection.Emit.

Prima di andare lì, prima controlla se quello che hai ora è in realtà troppo lento. La spesa per la riflessione sta scavando i metadati fuori dall'assemblea. Ciò è stato fatto quando hai creato il delegato, dopo di che le informazioni sul tipo sono state memorizzate nella cache.

+1

Quindi intendi che usare DynamicInvoke sul delegato generato nello snippet due è veloce o mi sbaglio? Attualmente sto usando SetValue/GetValue per scrivere/leggere le proprietà e volevo renderlo più veloce. – Giorgi

0

È possibile limitare il metodo generico a lavorare solo con i tipi di riferimento e crearne un altro funzionante solo con tipi di valori e decidere quale funzionalità utilizzare di conseguenza?

Problemi correlati