Inizializzazione della variabile membro C #; la migliore pratica?

È meglio inizializzare le variabili dei membri della class sulla dichiarazione

private List _things = new List(); private int _arb = 99; 

o nel costruttore predefinito?

 private List _things; private int _arb; public TheClass() { _things = new List(); _arb = 99; } 

È semplicemente una questione di stile o ci sono dei compromessi in termini di prestazioni, in un modo o nell’altro?

In termini di prestazioni, non vi è alcuna reale differenza; gli inizializzatori di campo sono implementati come logica del costruttore. L’unica differenza è che gli inizializzatori di campo avvengono prima di qualsiasi costruttore “base” / “questo”.

L’approccio del costruttore può essere utilizzato con proprietà auto-implementate (gli inizializzatori di campo non possono) – es

 [DefaultValue("")] public string Foo {get;set;} public Bar() { // ctor Foo = ""; } 

Oltre a questo, tendo a preferire la syntax dell’inizializzatore di campo; Trovo che mantenga le cose localizzate – es

 private readonly List items = new List(); public List Items {get {return items;}} 

Non devo andare a caccia su e giù per trovare dove è assegnato …

L’ovvia eccezione è dove è necessario eseguire una logica complessa o gestire i parametri del costruttore – nel qual caso l’inizializzazione basata sul costruttore è la strada da percorrere. Allo stesso modo, se si dispone di più costruttori, sarebbe preferibile che i campi venissero sempre impostati allo stesso modo – in modo da poter avere dei recettori come:

 public Bar() : this("") {} public Bar(string foo) {Foo = foo;} 

modifica: come commento a margine, si noti che in quanto sopra, se ci sono altri campi (non mostrati) con inizializzatori di campo, allora sono solo inizializzati direttamente nei costruttori che chiamano base(...) – cioè la public Bar(string foo) ctor. L’altro costruttore non esegue gli inizializzatori di campo, poiché sa che sono eseguiti da this(...) ctor.

In realtà, gli inizializzatori di campo come dimostrate sono una comoda abbreviazione. Il compilatore copia effettivamente il codice di inizializzazione all’inizio di ogni costruttore di istanze definito per il proprio tipo.

Questo ha due implicazioni: in primo luogo, ogni codice di inizializzazione del campo è duplicato in ciascun costruttore e, in secondo luogo, qualsiasi codice che includi nei costruttori per inizializzare i campi su valori specifici, infatti, riassegnerà i campi.

Quindi, per quanto riguarda le prestazioni, e per quanto riguarda la dimensione del codice compilato, è meglio spostare gli inizializzatori di campo nei costruttori.

D’altra parte, l’impatto sulle prestazioni e il codice “ingombrante” di solito sono trascurabili, e la syntax di inizializzazione dei campi ha l’importante vantaggio di ridurre il rischio che potresti dimenticare di inizializzare alcuni campi in uno dei tuoi costruttori.

Uno dei principali limiti con gli inizializzatori di campo è che non c’è modo di includerli in un blocco try-finally. Se viene lanciata un’eccezione in un inizializzatore di campo, tutte le risorse che sono state allocate nei precedenti inizializzatori verranno abbandonate; non c’è modo di impedirlo. Altri errori nella costruzione possono essere affrontati, se goffamente, facendo in modo che un costruttore della base protetta accetti un IDisposto per riferimento e lo indichi come prima operazione. Si può quindi evitare di chiamare il costruttore eccetto i metodi factory che, in caso di eccezione, chiameranno Dispose sull’object parzialmente creato. Questa protezione consentirà la pulizia di IDisposables creati negli inizializzatori di class derivata se il costruttore della class principale non riesce dopo aver “contrabbandato” un riferimento al nuovo object. Sfortunatamente, non c’è modo di fornire tale protezione se un inizializzatore di campo fallisce.

Utilizzare gli inizializzatori di campo o creare una funzione Init (). Il problema nel mettere queste cose nel tuo costruttore è che se hai mai bisogno di aggiungere un secondo costruttore, finisci con il codice copia / incolla (o lo trascuri e finisci con le variabili non inizializzate).

Avrei inizializzato dove dichiarato. Oppure i costruttori chiamano una funzione Init ().

Per esempio le variabili, è in gran parte una questione di stile (preferisco usare un costruttore). Per le variabili statiche, c’è un vantaggio in termini di prestazioni all’inizializzazione in linea (non sempre ansible, ovviamente).

Dipende davvero da te.
Inizialmente li inizializzo in linea, perché non mi piace avere un costruttore quando non ne ho davvero bisogno (mi piacciono le piccole classi!).

Al punto aggiunto a quanto sopra: si ha sempre un costruttore quando si implementano le classi che hanno un’implementazione. Se non si dichiara uno, l’istruttore predefinito viene dedotto dal compilatore [public Foo () {}]; un costruttore che non accetta argomenti.

Spesso mi piace offrire entrambi gli approcci. Consenti ai costruttori di coloro che desiderano utilizzarli e consentire agli Inizializzatori campo per le situazioni in cui desideri utilizzare un’implementazione semplificata o predefinita della class / tipo. Ciò aggiunge flessibilità al tuo codice. Tieni presente che chiunque può utilizzare l’inizializzatore di campo predefinito se lo desidera … assicurati di dichiararlo manualmente se offri più di un costruttore – pubblico Foo () {}