2015-09-08 10 views
5

Devo richiamare gli overload del metodo in base al tipo di oggetto in fase di esecuzione utilizzando le funzionalità di binding tardivo C#. Funziona bene quando tutti i sovraccarichi sono definiti nella stessa classe in cui sta avvenendo la chiamata. Ma quando un overload è definito in una classe derivata, non verrà vincolato al runtime.I sovraccarichi del metodo di binding in ritardo C# non funzionano quando il sovraccarico viene definito in una classe derivata

class BaseT 
{} 

class DerivedA : BaseT 
{} 

class DerivedB : BaseT 
{} 

class Generator 
{ 
    public void Generate(IEnumerable<BaseT> objects) 
    { 
     string str = ""; 
     foreach (dynamic item in objects) 
     { 
      str = str + this.Generate(item); //throws an exception on second item 
     } 
    } 

    protected virtual string Generate(DerivedA a) 
    { 
     return " A "; 
    }   
} 

class DerivedGenertor : Generator 
{ 
    protected virtual string Generate(DerivedB b) 
    { 
     return " B "; 
    } 
} 



class Program 
{ 
    static void Main(string[] args) 
    { 
     List<BaseT> items = new List<BaseT>() {new DerivedA(), new DerivedB()}; 
     var generator = new DerivedGenertor(); 
     generator.Generate(items); 
    } 
} 

Ecco un altro esempio più chiaro:

class BaseT 
{} 

class DerivedA : BaseT 
{} 

class DerivedB : BaseT 
{} 

class DerivedC : BaseT 
{ } 

class Generator 
{ 
    public void Generate(IEnumerable<BaseT> objects) 
    { 
     string str = ""; 
     foreach (dynamic item in objects) 
     { 
      str = str + this.Generate(item); //throws an exception on third item 
     } 
    } 

    public virtual string Generate(DerivedA a) 
    { 
     return " A "; 
    } 

    public virtual string Generate(DerivedC c) 
    { 
     return " C "; 
    } 
} 

class DerivedGenertor : Generator 
{ 
    public virtual string Generate(DerivedB b) 
    { 
     return " B "; 
    } 
} 



class Program 
{ 
    static void Main(string[] args) 
    { 
     List<BaseT> items = new List<BaseT>() {new DerivedA(), new DerivedC(), new DerivedB()}; 
     dynamic generator = new DerivedGenertor(); 
     generator.Generate(items); 
    } 
} 
+3

'DerivedGenertor.Generate' è' protected', quindi non è accessibile dalla classe 'Generator'. – PetSerAl

+0

Qual è esattamente il secondo elemento? – MikeG

risposta

3

Si avrebbe bisogno di dichiarare la Generator così dinamico e in modo da avere risoluzione dinamica sull'oggetto di ingresso e sul metodo di essere chiamato. Ma dovrai modificare i modificatori di accesso a public o protected internal per farlo, perché ora hai un metodo risolto esternamente.

class BaseT 
{ } 

class DerivedA : BaseT 
{ } 

class DerivedB : BaseT 
{ } 

class Generator 
{ 
    public string Generate(IEnumerable<BaseT> objects) 
    { 
     string str = ""; 
     dynamic generator = this; 
     foreach (dynamic item in objects) 
     { 
      str = str + generator.Generate(item); 
     } 
     return str; 
    } 

    protected internal virtual string Generate(DerivedA a) 
    { 
     return " A "; 
    } 
} 

class DerivedGenertor : Generator 
{ 
    protected internal virtual string Generate(DerivedB b) 
    { 
     return " B "; 
    } 
} 



class Program 
{ 
    static void Main(string[] args) 
    { 
     List<BaseT> items = new List<BaseT>() { new DerivedA(), new DerivedB() }; 
     var generator = new DerivedGenertor(); 
     string ret = generator.Generate(items); 
    } 
} 
+0

Non vedo perché CLR non sia in grado di individuare l'altro sovraccarico poiché il tipo di generatore di runtime ha effettivamente il sovraccarico corretto. Il CLR non cerca tra tutti i metodi per trovare quello giusto? È di progettazione? –

+0

@SiamakS. No. Questa è la definizione della differenza tra un legame tardivo e un legame precoce. CLR non cerca MAI in alcun modo. DLR fa. Devi dire esplicitamente che vuoi un binding tardivo, perché è molto più lento e più incline agli errori.Al momento della compilazione (early binding), il compilatore conosce solo 'Genera (DerivedA a)', quindi lo usa. – Aron

+0

@Aron Il compilatore non collega in anticipo nulla perché l'argomento è 'dinamico'. Se provasse a legare in fase di compilazione, darebbe un errore di compilazione in quanto non esiste un candidato valido; non ogni 'BaseT' è un' DerivedA'. Cambia 'item dinamico' in' var item' e il compilatore ti darà l'ovvio errore. Il fatto che l'errore sia in fase di runtime indica un'associazione tardiva. – InBetween

2

Come ti aspetti che si rileghi? Il compilatore associa la chiamata this.Generate(item) all'unico candidato possibile: Generator.Generate(DerivedA a). Questo non ha nulla a che fare con quando avviene l'associazione; DerivedGenerator.Generate(DerivedB b) non è considerato un candidato valido perché Generator non ha assolutamente alcuna idea della sua esistenza e si chiedono il metodo attraverso il tipizzazione statica Generator esempio this (faccio notare che il metodo essendo protected non è il problema, anche se fosse public la seconda chiamata fallire). Perché una classe base dovrebbe sapere qualcosa su un nuovo metodo virtuale definito in una classe derivata?

Per fare questo lavoro, si sia definito virtual Generate(DerivedB) nella classe base Generator e esclusione in DerivedGenerator o, se ciò non è un'opzione allora fate tutto risolvere in fase di esecuzione.

Nel tuo caso, come Brian sottolinea correttamente, si avrebbe bisogno di fare il Generator esempio dynamic anche al fine di consentire vincolante la chiamata Generate a DerivedGenerator.Generate quando appropriata. Altrimenti i candidati impostati saranno limitati solo a quelli Generator a conoscenza.

Questo vi obbligherà a ristrutturare in modo significativo il codice in quanto sarà necessario anche effettuare Generate almeno internal o internal protected.

Inoltre, dovrei notare che fare una chiamata a dynamic.Generate(dynamic) per far funzionare le cose sembra un grosso codice per me e probabilmente stai abusando del sistema di tipi di C#. Prenderò in considerazione il refactoring del tuo codice per una soluzione più sicura.

vi consiglio anche di leggere serie fantastica di Eric Lippert: Wizards and warriors spiegare come alcune gerarchie di oggetti non possono essere ben espressi con il sistema di tipo C# s 'e come si potrebbe (ma normalmente non dovrebbe) utilizzare dynamic per ottenere doppia spedizione in C# a per eludere alcuni dei suoi limiti.

+0

questo è un esempio di associazione tardiva che si verifica in fase di runtime perché l'articolo è dichiarato dinamico. Ma i sovraccarichi si trovano solo nella stessa classe in cui si sta verificando la chiamata. Accertati però che il collegamento avvenga in fase di esecuzione altrimenti il ​​compilatore si lamenterebbe perché nessun sovraccarico accetta BaseT. –

+0

@SiamakS. Ho modificato la mia risposta per chiarire alcuni punti importanti. – InBetween

Problemi correlati