2013-07-15 26 views
8

Sto tentando di definire un metodo di estensione che può restituire un oggetto di un tipo definito dalla chiamata.Come definire un metodo di estensione generico

Uso desiderata:Cat acat = guy.GiveMeYourPet<Cat>();

Tentativo implementazione

non ho problemi a definire metodi generici come questo:

public static T GiveMeYourPet<T>(Person a) { ... } 

Cat acat = GiveMeYourPet<Cat>(guy); 

o estensione metodi come questo:

public static Cat GiveMeYourPetCat<P>(this P self) where P : Person, ... { ... } 

Cat acat = guy.GiveMeYourPetCat(); 

Ma quando cerco di fare quello che voglio:

public static T GiveMeYourPet<T, P>(this P self) where P : Person, ... { ... } 

Cat acat = guy.GiveMeYourPet<Cat>(); 

Il compilatore si aspetta GiveMeYourPet() per ricevere 2 argomenti di tipo (anche se uno è implicita chiamando il metodo di estensione sull'oggetto guy.

Cosa posso fare per fare in modo che funzioni?

Nota che ho provato anche invertendo l'ordine in cui sono definiti i parametri, ma non cambia nulla:

public static T GiveMeYourPet<P, T>(this P self) 

la seguente chiamata, inoltre, non funziona, perché non si può avere una chiamata di metodo nel tipo specifiation:

Cat acat = guy.GiveMeYourPet<guy.GetType(), Cat>(); 
+1

Sto pensando che avresti bisogno di passare a T in modo esplicito come parametro - altrimenti come sarà il compilatore sa cosa tipo di tornare? – Tim

+1

Non è possibile rendere impliciti solo alcuni parametri. È tutto o nessuno. – Andre

+0

Non penso che i metodi di estensione funzionino con un tipo generico 'this'; la prima soluzione dovrebbe funzionare comunque. – poke

risposta

4

Il tipo C# compilatore inferenza non è sofisticato come si potrebbe sperare. È necessario specificare in modo esplicito di entrambi i tipi in tale metodo:

void Main() 
{ 
    int i = 0; 
    bool b = i.GiveMeYourPet<bool, int>(); 
} 

public static class MyExtensions 
{ 
    public static T GiveMeYourPet<T, P>(this P self) 
    { 
     return default(T); 
    } 
} 

Se si vuole evitare di specificare sia esplicitamente (e non mi biasimo), si potrebbe tentare di cambiare il metodo a qualcosa di simile:

public static T GiveMeYourPet<T>(this IPetOwner self) 

(con questa interfaccia, non si dovrebbe nemmeno bisogno di sapere qual è il tipo reale è, se non, usa as o is) o anche:

public static T GiveMeYourPet<T>(this object self) 

(e utilizzare as o is)

Se questo non è un'opzione, e il vero tipo di guy (nel tuo esempio) non è noto in modo statico (per esempio basta averlo come un object), probabilmente dovrete usare riflessione, ad esempio:

MethodInfo method = typeof(MyExtensions).GetMethod("GiveMeYourPet"); 
MethodInfo generic = method.MakeGenericMethod(typeof(Pet), guy.GetType()); 
generic.Invoke(guy, null); 
+0

Non solo, ma cosa succede se non conosco il tipo di 'ragazzo' (o io in questa istanza), come posso passare il tipo del parametro 'this'? – Alain

+0

@Alain la mia risposta è stata aggiornata con più opzioni. –

+0

@Alain 'dynamic' è in realtà una caratteristica linguistica abbastanza utile. Guarda la mia risposta. –

1

Se qualcosa del genere guy.GiveMeYour.Pet<Cat>(); avrebbe funzionato si può costruire 2 livelli simili a Codice:

public class GiveMeYourBuilder<P> 
{ 
    public P Me {get;set;} 
    public T Pet<T>() : where T: new() 
    { return new T();} 
} 

public static PetExtensions 
{ 
    public GiveMeYourBuilder<P>(this P me) 
    { 
     return new GiveMeYourBuilder<P> { Me = me;} 
    } 
} 
1

È non può in parte specificare argomenti generici, o sono tutti dedotti o devi specificarli tutti.In questo caso, il più vicino si può ottenere è probabilmente per restituire un oggetto intermedio che porta il Person tipo generico il metodo di estensione è chiamato, e definire i Get metodi su questo:

public class GiveContext<T> where T : Person 
{ 
    public P MeYourPet<P>() where P : Pet 
    { 
     return default(P); 
    } 
} 

public static GiveContext<T> Give<T>(this T person) where T : Person 
{ 
    return new GiveContext<T>(); 
} 

che si può usare come:

var p = new Person(); 
Cat c = p.Give().MeYourPet<Cat>(); 
+0

Se si dispone di un vincolo 'Persona' su' Dare ', potrebbe anche avere un 'questa persona Persona' su un'estensione. Non mi sembra che risolva nulla visto che è stato il primo tentativo della domanda originale –

0

Non è possibile farlo, sfortunatamente. Se il compilatore non riesce a visualizzarli tutti tutti fuori, è necessario digitare tutti gli argomenti di tipo. Il compilatore C# non è che smart. dynamic può aiutare però:

public static T GiveMeYourPet<T>(this dynamic self) 
{ 
    //in here check that self meets your constraints using is, as, etc. 
} 
Problemi correlati