2009-12-09 9 views
6

Nel quadro che stiamo costruendo ci serve il seguente schema:caso Corner in usando l'espressione lambda a base di costruttore

public class BaseRenderer 
{ 
    Func<string> renderer; 
    public BaseRenderer(Func<string> renderer) 
    { 
     this.renderer = renderer; 
    } 

    public string Render() 
    { 
     return renderer(); 
    } 
} 

public class NameRenderer : BaseRenderer 
{ 
    public string Name{ get; set; } 

    public NameRenderer() 
     : base(() =>this.Name) 
    {} 
} 

Come vedete, creiamo un lambda quando si chiama il costruttore di base.

public class Program 
{ 
    public static void Main() 
    { 
     Console.WriteLine(new NameRenderer(){Name = "Foo"}.Render()); 
    } 
} 

Stranamente, quando si cerca di utilizzare effettivamente il lambda getta NullReferenceException (applicazione console), o qualche tipo di ExecutionEngineExceptionexception (app Web su IIS).

Penso che il motivo sia che questo puntatore non è pronto prima di chiamare il costruttore di base, quindi lambda non è in grado di acquisire this.Name in questa fase.

Non dovrebbe generare un'eccezione in "tempo di acquisizione" anziché "tempo di esecuzione"? Questo comportamento è documentato?

Posso rifattorizzare il codice in un modo diverso, ma penso che valga un commento.

+2

Questo è stato chiesto prima, verrà risolto in C# 4.0 – leppie

risposta

22

Come sottolinea correttamente, questo non dovrebbe essere legale secondo le specifiche. Abbiamo accidentalmente permesso a questo falso utilizzo di introdursi furtivamente dal rilevatore di errori che cerca usi errati di "questo" prima che sia legale farlo. Ho risolto il bug; il compilatore C# 4 contrassegna correttamente il tuo programma come un errore.

Molte scuse per l'inconveniente; questo è stato un mio errore

+6

hah, non capita spesso che a) ti imbatti in un bug nel compilatore e b) il responsabile responsabile risponda alla tua domanda con un mea culpa :-P dolce! –

+1

Il team C# è fantastico, grazie Eric. – Olmo

4

Penso che tu abbia ragione. La sottoclasse non è ancora costruita quando viene chiamato il costruttore della classe base e quindi l'accesso ai membri nella sottoclasse fornisce un riferimento null. CLR non ha modo di sapere in fase di compilazione se l'istanza esiste o meno.

Lo spostamento della logica nel corpo del costruttore dovrebbe risolvere il problema.

6

La specifica C# in 7.5.7 afferma: "Un accesso di questo tipo è consentito solo nel blocco di un costruttore di istanze, un metodo di istanza o un accessor di istanza."

E ancora più direttamente in 10.11.1: "Un inizializzatore del costruttore di istanze non può accedere all'istanza che si sta creando.Pertanto è un errore in fase di compilazione fare riferimento a questo in un'espressione argomento del programma di inizializzazione del costruttore, in quanto è una compilazione -time errore per un'espressione argomento per fare riferimento a qualsiasi membro dell'istanza tramite un nome semplice. "

Sebbene l'istanza sia stata creata in base alla 7.5.10.

Hmm. Questo è in realtà piuttosto strano. Non ho visto alcun errore in fase di compilazione.

+0

Non avevo proprio ragione la prima volta. C'è un'istanza al momento, ma non hai accesso ad essa. Le risposte sono aggiornate. – asgerhallas

+5

Non si è verificato un errore di compilazione perché il compilatore ha avuto un bug. Dovrebbe essere un errore, e in C# 4, lo è. –

2

Il lambda ha catturato il valore di "questo" e catturato null poiché l'oggetto non era ancora stato costruito. Questo mi sembra un bug del compilatore, dovrebbe aver generato un errore per questo. Codice come questo normalmente genera un CS0027 (la parola chiave 'questo' non è disponibile nel contesto corrente) o CS0120 (è richiesto un riferimento all'oggetto). Scommetto che non è facile da implementare.

Anyhoo, il codice non funziona. La classe NameRenderer richiede un costruttore con un argomento stringa in modo da poter inizializzare la classe base.

1

non è legale : base(()=>this) legale? Puoi fare : this() quindi un riferimento a questo sembra andare bene, solo non le proprietà su di esso.Il fatto che : base(()=>this) non sia più legale ha appena rotto alcune applicazioni a funzioni parziali che ho fatto durante la costruzione. Può essere risolto spostandolo nel corpo del costruttore, ma c'è una differenza di ordine: la classe base non può più essere passata in modo trasparente un'applicazione a sé stessa (poiché il costruttore della classe base viene chiamato prima del corpo del costruttore della sottoclasse).

Problemi correlati