2011-08-18 17 views
5

costruttori siano eseguiti nell'ordine dall'alto verso il basso I.E. il primo di base seguito da uno derivato. Questo arangement si basa su un'importante assicurazione OOP che un oggetto (base qui) deve essere sempre inizializzato prima di poter essere utilizzato (qui nel costruttore della classe derivata).Perché gli inizializzatori di campo di una classe derivata vengono eseguiti prima che gli inizializzatori delle classi base

Mi chiedo perché gli inizializzatori di campo non seguano questo principio in C#? Mi sto perdendo qualcosa qui?

Ho trovato un'utilità di questo principio anche con gli inizializzatori di campo. Ho una classe base con una proprietà che restituisce l'oggetto Identity. Ogni classe derivata ha il proprio campo di repository che sto inizializzando usando l'inizializzatore di campo (usando il costruttore di default). Recentemente ho deciso che anche la classe repository doveva essere dotata di oggetto Identity, quindi ho introdotto un argomento extra nel costruttore del repository. Ma io sono bloccato per scoprire:

public class ForumController : AppControllerBase 
{ 
     ForumRepository repository = new ForumRepository(Identity); 
    // Above won't compile since Identity is in the base class. 

    // ... Action methods. 
} 

Ora sono lasciato con una sola opzione che è grassoccio ogni mio controller con un costruttore di default solo per fare il lavoro di inizializzazione dell'oggetto repository con Identità.

+0

No, il motivo per cui non viene compilato non è affatto che 'Identity' è nella classe base, è semplicemente perché è un membro di istanza. Quindi, la domanda nel titolo non è rilevante per quello che stai cercando di fare ... – Guffa

risposta

6

Perché gli inizializzatori di campo di una classe derivata vengono eseguiti prima degli inizializzatori del campo della classe base?

Buona domanda. Ho risposto alla tua domanda in questi post del blog a partire dal 2008:

http://blogs.msdn.com/b/ericlippert/archive/2008/02/15/why-do-initializers-run-in-the-opposite-order-as-constructors-part-one.aspx

http://blogs.msdn.com/b/ericlippert/archive/2008/02/18/why-do-initializers-run-in-the-opposite-order-as-constructors-part-two.aspx

0

Gli inizializzatori di campo vengono eseguiti nel costruttore e, poiché il costruttore della classe base viene chiamato per primo, tutti gli inizializzatori di campo vengono eseguiti anche prima dell'esecuzione del costruttore derivato.

Esempio:

public class Base { 

    // field initialiser: 
    private string _firstName = "Arthur"; 

    public string FirstName { get { return _firstName;}} 
    public string LastName { get; private set; } 

    // initialiser in base constructor:  
    public Base() { 
    LastName = "Dent"; 
    } 

} 

public class Derived : Base { 

    public string FirstNameCopy { get; private set; } 
    public string LastNameCopy { get; private set; } 

    public Derived() { 
    // get values from base class: 
    FirstNameCopy = FirstName; 
    LastNameCopy = LastName; 
    } 

} 

prova:

Derived x = new Derived(); 
Console.WriteLine(x.FirstNameCopy); 
Console.WriteLine(x.LastNameCopy); 

uscita:

Arthur 
Dent 
+0

Penso che tu non abbia capito il mio punto. So già cosa stai dicendo.La mia domanda è data dagli inizializzatori di campo in entrambe le classi di base e derivate, perché quella di una classe derivata viene eseguita prima e poi quella di base? Se vedi i costruttori è esattamente l'opposto. –

+0

@Varun K: Vedo, si sta tentando di accedere a un campo da un inizializzatore di campo. Non è possibile farlo perché non è possibile utilizzare i membri di istanza da un inizializzatore e ciò non ha nulla a che fare con i campi che si trovano in classi diverse. – Guffa

+0

So che non posso farlo :-). ma volevo sapere il motivo dietro di esso. –

0

Inizializzatori di campo non sono destinate ad essere un sostituto per i costruttori.

Come da documentazione MSDN, tutti gli inizializzatori di campo vengono eseguiti prima dei costruttori. Tuttavia una restrizione sul Field Initializer è che non possono fare riferimento ad altri campi di istanza. (http://msdn.microsoft.com/en-us/library/ms173118(v=vs.80).aspx)

La limitazione è dovuta al fatto che non è possibile identificare l'ordine corretto e le dipendenze per eseguire gli Inizializzatori campo a livello di compilatore.

Dovresti scrivere un costruttore predefinito per ottenere ciò che desideri. Potresti provare con un campo statico, comunque; è una cattiva pratica e potrebbe non adattarsi al tuo design.


Modifica per chiarire domanda posta in commento:

Per quanto riguarda una classe derivata è interessato, i campi di classe di base lo stesso aspetto se vengono inizializzati tramite l'inizializzatore o il costruttore. Questa informazione non può essere fornita alle classi figlie in quanto ciò significherebbe esporre l'implementazione della classe base (che è strettamente privata alla classe base).

Ciò implica che la classe derivata Initializer non ha modo di capire se tutti i campi della classe base sono stati inizializzati prima di accedervi. Quindi il comportamento non è permesso.

+0

Conosco già questo punto di restrizione (ordine e dipendenza corretti) ma, se vedete, questo è strettamente applicabile ai campi della stessa classe. Sto interrogando in vista di inizializzatori di campi in inizializzatore di base e di campo nella classe derivata –

2

In C#, un costruttore corre la sequenza:

 
    Perform all field initializers 
    Chain to base class constructor 
    Execute user-supplied code for constructor 

in vb.net, la sequenza è:

 
    Chain to base class constructor 
    Perform all field initializers 
    Execute user-supplied code for constructor 

Non c'è Framework-base motivo per cui C# fa le cose nell'ordine che fa, come dimostra il fatto che vb.net può e le fa in una sequenza diversa. La giustificazione del progetto per l'approccio C# è che non dovrebbe esserci alcuna possibilità che un oggetto venga esposto al mondo esterno prima che tutti gli inizializzatori di campo (per i campi derivati ​​e di classe base) siano stati eseguiti; poiché un costruttore della classe base potrebbe esporre un oggetto al mondo esterno, l'applicazione di tale requisito implica che gli inizializzatori del campo debbano essere eseguiti prima del costruttore della classe base.

Personalmente, non trovo questa giustificazione particolarmente convincente. Esistono molti scenari in cui non sarà possibile impostare i campi su valori utili senza avere informazioni che non saranno disponibili prima dell'esecuzione del costruttore base. Qualsiasi codice il cui costruttore di base possa esporre istanze parzialmente costruite deve essere preparato per tale possibilità. Mentre ci sono momenti in cui sarebbe utile specificare che un inizializzatore di campo dovrebbe essere eseguito "in anticipo", penso che ci siano molte altre situazioni in cui è utile per loro poter accedere all'oggetto di fantasia (in qualche misura perché credo che i campi di classe i cui valori dovrebbero essere considerati come invarianti durante la vita di un'istanza di classe dovrebbero, quando è pratico, essere impostati in modo dichiarativo tramite inizializzatori piuttosto che imperativamente all'interno di un costruttore).

Per inciso, una funzione che mi piacerebbe vedere in entrambi vb.net e C# sarebbe un mezzo per dichiarare campi inizializzati da parametri e pseudo-campi. Se una classe ha un campo inizializzato con parametri di un certo nome e tipo, ogni costruttore per quella classe che non fa catena ad un altro della stessa classe deve contenere parametri con i nomi e i tipi appropriati. I valori di quei campi verrebbero impostati nel costruttore prima di ogni altra cosa e sarebbero accessibili ad altri inizializzatori di campo. Gli pseudo-campi si comporterebbero in modo sintattico come i campi, eccetto che sarebbero solo utilizzabili negli inizializzatori di campo e sarebbero implementati come variabili locali all'interno dei costruttori. Una tale caratteristica renderebbe più convenienti molti tipi di strutture. Ad esempio, se un tipo è dovuto tenere un caso particolare matrice tutta la sua durata, potendo fare:

 
    readonly param int Length; 
    readonly ThingType[] myArray = new ThingType[Length]; 

sembrerebbe bello di dover costruire la serie sia nel costruttore della classe (che non potrebbe accadere fino a dopo l'esecuzione del costruttore base) o (per vb.net) dovendo passare la lunghezza a un costruttore di classe base che potrebbe quindi utilizzarlo per impostare un campo (che occuperebbe quindi spazio nella classe anche se il suo valore-- come Length sopra - potrebbe essere ridondante).

Problemi correlati