2011-01-07 5 views
8

J'ai défini les classes et méthodes suivantes:Pourquoi C# ne se lie-il pas correctement aux méthodes génériques surchargées?

using System; 
using System.Linq.Expressions; 
using System.Windows.Forms; 

public class ReturnValue<T, S> {} 

public class Something<T> 
{ 
    // Sorry about the odd formatting. Trying to get it to fit nicely... 
    public ReturnValue<T, C> 
    Do<C, S>(C control, Expression<Func<C, S>> controlProperty) 
    where C : Control 
    { 
     return new ReturnValue<T, C>(); 
    } 

    public ReturnValue<T, ToolStripItem> 
    Do<S>(ToolStripItem control, Expression<Func<ToolStripItem, S>> controlProperty) 
    { 
     return new ReturnValue<T, ToolStripItem>(); 
    } 
} 

Cette compile bien. Woo hoo! À mi-chemin. Ensuite, j'essaie de l'utiliser plus tard avec le code comme ceci:

var toolStripItem = new ToolStripStatusLabel(); 

var something = new Something<string>(); 
something.Do(toolStripItem, t => t.Text); // Does not compile 

Ceci, cependant, meurt avec le message d'erreur suivant

Le type ToolStripStatusLabel ne peut pas être utilisé comme paramètre de type C dans le type générique ou méthode Something<T>.Do<C,S>(C, Expression<Func<C,S>>). Il n'y a pas de conversion de référence implicite de ToolStripStatusLabel à Control.

Il me semble que le compilateur C# a échoué dans ce cas, bien que les deux méthodes ne créent pas un ensemble de déclarations de méthode ambiguës. Control et ToolStripStatusLabel existent en tant que frères et sœurs dans l'arbre d'héritage de Component. Je pense que le compilateur aurait assez d'informations pour lier correctement l'invocation de méthode dans le code client.

Cependant, si je fais la même chose avec mes propres classes de frères et sœurs, alors tout se compile bien.

public class Parent {} 
public class Child1 : Parent {} 
public class Child2 : Parent {} 

public class Something2<T> 
{ 
    public ReturnValue<T, C> 
    Do<C, S>(C control, Expression<Func<C, S>> controlProperty) 
    where C : Child1 
    { 
     return new ReturnValue<T, C>(); 
    } 

    public ReturnValue<T, Child2> 
    Do<S>(Child2 control, Expression<Func<Child2, S>> controlProperty) 
    { 
     return new ReturnValue<T, Child2>(); 
    } 
} 

var child2 = new Child2(); 
var something2 = new Something2<string>(); 
something2.Do(child2, c => c.GetType()); // Compiles just fine 

Quelqu'un peut-il faire la lumière sur ce que j'ai fait de mal, si quelque chose?

Répondre

11

Le problème est que la première méthode est dans le candidat fixé pour la résolution de surcharge, parce que la contrainte de type C : Control est appliqué uniquement après une résolution de surcharge a été effectuée. Je crois que vous vous attendez à ce qu'il soit éliminé tôt - et ce n'est pas le cas. Maintenant, si vous traitez C = ToolStripItem, la première surcharge est plus spécifique que la seconde - donc le résultat de la résolution de surcharge est de choisir cette première version.

La validation de contrainte de type est puis appliquée ... et échoue.

J'ai un blog post on this matter qui peut vous aider à comprendre le processus, puis another blog post où j'applique les règles d'une manière plutôt bête.

EDIT: Dans votre deuxième exemple, le type de l'argument est exactement le type spécifié dans le premier paramètre, de sorte que la première méthode ne finit pas par être plus spécifique. La deuxième méthode gagne en raison de la réduction des paramètres de type (je pense, je ne l'ai pas vérifiée en détail) et est ensuite validée et réussie.

Pour le remettre en termes ToolStripItem, vous pouvez réellement faire votre premier échantillon compiler avec un simple changement:

// Change this 
var toolStripItem = new ToolStripStatusLabel(); 
// To this... 
ToolStripItem toolStripItem = new ToolStripStatusLabel(); 

Modification du type de toolStripItem de ToolStripStatusLabel à ToolStripItem compilation enlève le « avantage » que la première méthode avait, donc il compile.

+0

Jon, c'est une excellente explication. Je vous remercie. – realistschuckle

+0

Alors pourquoi le deuxième exemple compile-t-il? –

+0

BlueRaja (en plus d'un grand nom) écrit correctement, que j'ai pensé après avoir considéré l'explication plus loin. Un aperçu, Jon? – realistschuckle

0

Je pense que vous avez juste besoin d'être plus explicite avec votre appel:

var toolStripItem = new ToolStripStatusLabel(); 
var something = new Something<string>(); 
something.Do<string>(toolStripItem, t => t.Text); // might compile 
Questions connexes