2011-01-07 15 views
8

ho definito le seguenti classi e metodi:Perché C# non si associa correttamente ai metodi sovrascritti generici?

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

Questo compila bene. Woo hoo! A metà strada lì. Poi, cerco di utilizzare in un secondo momento con il codice come questo:

var toolStripItem = new ToolStripStatusLabel(); 

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

Questo, però, muore con il seguente messaggio di errore

Il tipo ToolStripStatusLabel non può essere utilizzato come tipo di parametro C nel tipo generico o metodo Something<T>.Do<C,S>(C, Expression<Func<C,S>>). Non esiste alcuna conversione implicita del riferimento da ToolStripStatusLabel a Control.

Mi sembra che il compilatore C# abbia fallito in questo caso sebbene i due metodi non creino un insieme di dichiarazioni di metodi ambigue. Control e ToolStripStatusLabel esistono come fratelli nell'albero di ereditarietà di Component. Penserei che il compilatore avrebbe abbastanza informazioni per associare correttamente l'invocazione del metodo nel codice client.

Tuttavia, se faccio la stessa cosa con le mie classi di pari livello, quindi tutto va bene.

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 

Qualcuno può far luce su ciò che ho fatto di sbagliato, semmai?

risposta

11

Il problema è che il primo metodo è nel candidato set per la risoluzione di sovraccarico, in quanto il tipo di vincolo C : Control viene applicata solo dopo la risoluzione di sovraccarico. Credo che ti aspetti che venga eliminato presto - e non lo è.

Ora, se si tratti di C = ToolStripItem, il primo sovraccarico è più specifico del secondo - quindi il risultato della risoluzione di sovraccarico è scegliere quella prima versione.

La convalida del tipo di convalida è quindi applicata ... e non riesce.

Ho un blog post on this matter che può aiutare a capire il processo, e quindi another blog post dove applico le regole in un modo piuttosto stupido.

MODIFICA: nel secondo esempio, il tipo di argomento è esattamente il tipo specificato nel primo parametro, quindi il primo metodo non risulta più specifico. Il secondo metodo vince a causa del minor numero di parametri di tipo (credo, non l'ho verificato in dettaglio) e viene quindi convalidato e superato.

per rimetterlo in termini ToolStripItem, si potrebbe effettivamente fare il vostro primo campione compilato con una semplice modifica:

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

Modifica del tipo in fase di compilazione della toolStripItem da ToolStripStatusLabel a ToolStripItem toglie il "vantaggio" che il primo metodo aveva, quindi compila.

+0

Jon, questa è un'ottima spiegazione. Grazie. – realistschuckle

+0

Quindi, perché compila il secondo esempio? –

+0

BlueRaja (oltre ad un grande nome) scrive correttamente, cosa a cui ho pensato dopo aver considerato la spiegazione ulteriormente. Qualche idea, Jon? – realistschuckle

0

Penso solo bisogno di essere più esplicito con la chiamata:

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