2013-07-15 19 views
8

Ich versuche, eine Erweiterungsmethode zu definieren, die ein Objekt eines vom Aufruf definierten Typs zurückgeben kann.Wie kann ich eine generische Erweiterungsmethode definieren

Wunsch Gebrauch:Cat acat = guy.GiveMeYourPet<Cat>();

Versuchte Implementierung

Ich habe keine Probleme definieren, generische Methoden wie folgt aus:

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

Cat acat = GiveMeYourPet<Cat>(guy); 

oder Erweiterungsmethoden wie folgt aus:

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

Cat acat = guy.GiveMeYourPetCat(); 

Aber wenn ich versuche zu tun, was ich wirklich will:

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

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

Der Compiler erwartet GiveMeYourPet() 2 Typargumente zu erhalten (auch wenn man durch den Aufruf der Erweiterung Methode für das Objekt guy implizit zur Verfügung gestellt.

Was kann ich tun, damit dies funktioniert?

Bitte beachte, dass ich auch die Reihenfolge versucht haben Umkehr in der die Parameter definiert sind, aber es ändert sich nichts:

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

Der folgende Aufruf funktioniert auch nicht, weil Sie nicht einen Methodenaufruf in der Art haben kann Spezifikation:

+1

Ich denke, Sie bräuchten in T als Parameter explizit übergeben - wie sonst wird der Compiler wissen, was zurückkehren zu geben? – Tim

+1

Sie können nicht nur einige Parameter als implizit definieren. Es ist alles oder nichts. – Andre

+0

Ich glaube nicht, dass Erweiterungsmethoden mit einem generischen "this" -Typ arbeiten; Die erste Lösung sollte jedoch funktionieren. – poke

Antwort

4

Die C# -Compiler-Typ Inferenz ist nicht so anspruchsvoll wie Sie vielleicht hoffen. Sie müssen explizit beide Typen in einem solchen Verfahren angeben:

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

Wenn Sie vermeiden möchten, spezifizieren beide explizit (und ich würde es dir nicht verdenken), Sie könnten versuchen, Ihre Methode, um etwas zu ändern, wie:

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

(mit dieser Schnittstelle, sollten Sie nicht einmal wissen müssen, was die wirkliche Art ist, wenn Sie das tun, as oder is verwenden) oder auch:

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

(und Verwenden Sie as oder is)

Wenn das keine Option ist, und der echte Typ von guy (in Ihrem Beispiel) ist nicht statisch bekannt (z. B. Sie haben ihn nur als object), werden Sie wahrscheinlich werden müssen Reflexion verwenden, zB:

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

Nicht nur das, aber was ist, wenn ich nicht den Typ von "Typ" kenne (oder ich in diesem Fall), wie kann ich den Typ des 'This' Parameters übergeben? – Alain

+0

@Alain meine Antwort wurde mit mehr Optionen aktualisiert. –

+0

@Alain 'dynamic' ist eigentlich eine ziemlich nützliche Sprachfunktion. Sieh dir meine Antwort an. –

1

Wenn so etwas wie guy.GiveMeYour.Pet<Cat>(); funktionieren würde Sie 2 Ebenen ähnlich wie Code aufbauen können:

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

Sie Generische Argumente können nicht teilweise angegeben werden, entweder werden sie alle abgeleitet oder Sie müssen sie alle angeben.In diesem Fall ist die nächste man bekommen kann wahrscheinlich ein Zwischen Objekt zurückgeben, das trägt die allgemeine Person Art der Erweiterung Methode aufgerufen wird, und definieren Sie Ihre Get Methoden dazu:

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

, die Sie mögen können:

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

Wenn Sie eine 'Person'-Einschränkung auf' Give 'haben, könnte auch eine' this Person person' auf einer Erweiterung vorhanden sein. Ich habe keine Lust, etwas zu lösen, da es der erste Versuch der ursprünglichen Frage war –

0

Sie können dies leider nicht tun. Wenn der Compiler sie alle nicht herausfinden kann, müssen Sie alle Typenargumente eingeben. Der C# -Compiler ist nicht , dass Smart. dynamic kann jedoch helfen:

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