2013-07-24 6 views
5

Supponiamo ho definito Func come segue:Get tipo di ritorno "oggetto" da Func <MyClass, object>

Func<MyClass, object> f = o => o.StringProperty; 

o

Func<MyClass, object> f = o => o.Property.SomeMethod(); 

c'è modo per ottenere l'attuale ritorno digitare senza specificarlo?

+0

"Ottieni" il tipo di ritorno esattamente come? Un esempio ipotetico? – Jon

+0

@Jon Penso che dedurre il tipo di ritorno basato sul fatto che il compilatore lo sa ('stringa' nel primo esempio, il ritorno di' SomeMethod' nel secondo), tuttavia questo esempio collasserebbe a run-time come la variabile 'f 'potrebbe essere riassegnato. Ipoteticamente, se 'f' non può essere riassegnato, è possibile determinare staticamente il tipo di ritorno analizzando la definizione' Func'. Sembra un candidato per Roslyn ;-) –

+0

A seconda del tuo utilizzo, forse puoi sfruttare l'API 'Expression' per verificare il tipo di ritorno: http://stackoverflow.com/questions/671968/retrieving-property-name-from- espressione lambda (questa non è un'implementazione esatta di ciò che stai cercando di fare, solo un esempio correlato) –

risposta

4

È possibile ottenere il tipo di ritorno come questo:

Ma questo si tornerà il tipo object. Se si desidera ottenere Method o String o qualcosa che deriva da object, non sarà possibile avere le informazioni a meno che non si chiami il metodo.

In realtà si potrebbe, ma ciò significherebbe che si dovrebbe smontare il metodo core e quindi analizzarlo per vedere cosa può restituire il metodo. Ma anche in questo caso, potrebbero esserci molti tipi diversi di rendimento.

Quindi la risposta è: se vuoi sapere che restituisce un oggetto, allora sì puoi, altrimenti non ne vale la pena, ed è meglio trovare un altro modo di fare ciò che ti serve.

1

Si può fare qualcosa di simile a questo utilizzando un metodo generico per rendere il compilatore dedurre gli argomenti di tipo:

static Func<T1, R> Infer<T1, R>(Func<T1, R> f) { return f; } 

E poi:

var func = Infer((string s) => s.Length); 

Questa codificherà il tipo di ritorno in tipo di func al momento della compilazione.

Naturalmente per una soluzione più generalmente applicabile si avrebbe bisogno di un po 'di sovraccarichi di Infer per coprire Action e Func con uno, due, tre, ecc argomenti.

Se si desidera ottenere il tipo di ritorno in fase di esecuzione, per qualsiasi tipo di Func è semplice come func.Method.ReturnType come già rilevato da ppetrov.

+0

funziona fino a quando non abbiamo già creato l'istanza di Func. –

+0

@ EugeneD.Gubenkov: Ovviamente, se hai già 'Func f = s => s.Length' e fai' Infer (f) 'otterrai un' Func '- non c'è modo di recuperare le informazioni sul tipo che sono state" perse ". – Jon

+1

@ EugeneD.Gubenkov: Se vuoi fare di più allora devi lavorare con 'Expression >' e reflection. – Jon

2

Poiché si richiamano questi delegati Func<MyClass, object> in fase di runtime da altre fonti, le informazioni sul tipo vengono sostanzialmente perse.

Invece, in cui vengono definite queste funzioni, è possibile avere i chiamanti in sostanza, di codificare le informazioni di tipo in un delegato avvolto sfruttando la LINQ Expression API (EDIT: mi sciocco, di gran lunga più semplice, a questo punto, abbiamo già avere le informazioni momento della compilazione generico):

public class MyClassDelegate 
{ 
    private readonly Func<MyClass, object> Function; 

    public Type ReturnType { get; private set; } 

    private MyClassDelegate(Func<MyClass, object> function, Type returnType) 
    { 
     this.Function = function; 
     this.ReturnType = returnType; 
    } 

    public object Invoke(MyClass context) 
    { 
     return Function(context); 
    } 

    public static MyClassDelegate Create<TReturnType>(Func<MyClass, TReturnType> function) 
    { 
     Func<MyClass, object> nonTypedFunction = o => function(o); 
     return new MyClassDelegate(nonTypedFunction, typeof(TReturnType)); 
    } 
} 

(Un generico classe derivata MyClassDelegate<TReturnType> : MyClassDelegate potrebbe essere fatto anche per aggirare alcuni dei sillyness nel metodo Create, o evitare il valore-tipo di boxe, o di avere il ritorno scrivi le informazioni disponibili al momento della compilazione o anche riflettendo su qualsiasi cosa sia MyClassDelegate<TReturnType>.)

chiamanti che definiscono i delegati invece di lavorare direttamente con un Func<MyClass, object> avrebbe invece lavorare con questa classe e definire i loro delegati come:

MyClassDelegate f1 = MyClassDelegate.Create(o => o.StringProperty); 
MyClassDelegate f2 = MyClassDelegate.Create(o => o.Property.SomeMethod()); 

tuo API richiederebbe un MyClassDelegate, con il quale si può facilmente accedere ai loro tipi :

Console.WriteLine(f1.ReturnType.FullName); //string 
Console.WriteLine(f2.ReturnType.FullName); //whatever `SomeMethod()` is declared to return 

Infine, è possibile richiamare i delegati o addirittura creare Func<MyClass, object> delegati ancora:

f1.Invoke(myClassInstance); 
Func<MyClass, object> f3 = f1.Invoke; 
+0

@ EugeneD.Gubenkov: ho modificato la mia risposta; l'API di espressione LINQ non è necessaria a questo punto. Il takeaway fondamentale è che i chiamanti codifichino queste informazioni quando viene creato il delegato e sfruttano un delegato spostato per passare le informazioni attraverso l'API. –

+0

Grazie per la tua risposta dettagliata. Quando vogliamo fare queste cose correttamente dovremmo usare comunque le espressioni, capisco. –

+0

L'utilizzo di 'Expression' è utile solo se si desidera evitare l'inferenza generica. Cioè, potresti avere 'Crea (Espressione > funzione)' e determinare ancora il tipo usato. Ma si può anche sfruttare l'inferenza di tipo generico che ho usato nel mio aggiornamento. –