Domanda

È meglio inizializzare le variabili dei membri della classe sulla dichiarazione

private List<Thing> _things = new List<Thing>();
private int _arb = 99;

o nel costruttore predefinito?

private List<Thing> _things;
private int _arb;

public TheClass()
{
  _things = new List<Thing>();
  _arb = 99;
}

È semplicemente una questione di stile o ci sono compromessi prestazionali, in un modo o nell'altro?

È stato utile?

Soluzione

In termini di prestazioni, non c'è vera differenza; gli inizializzatori di campo sono implementati come logica del costruttore. L'unica differenza è che gli inizializzatori di campo avvengono prima di ogni "base" / "" presente ". costruttore.

L'approccio del costruttore può essere utilizzato con proprietà implementate automaticamente (gli inizializzatori di campo non possono), ovvero

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

A parte questo, tendo a preferire la sintassi dell'inizializzatore di campo; Trovo che mantenga le cose localizzate - cioè

private readonly List<SomeClass> items = new List<SomeClass>();
public List<SomeClass> 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 vengano sempre impostati allo stesso modo, quindi si potrebbero avere agenti come:

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

modifica: come commento laterale, nota che in precedenza, se ci sono altri campi (non mostrati) con inizializzatori di campo, vengono inizializzati solo direttamente nei costruttori che chiamano base (...) - ovvero il ctor public Bar (string foo) . L'altro costruttore non esegue inizializzatori di campo, poiché sa che sono stati eseguiti dal questo (...) ctor.

Altri suggerimenti

In realtà, gli inizializzatori di campo, come dimostrato, sono una scorciatoia conveniente. Il compilatore copia effettivamente il codice di inizializzazione all'inizio di ogni costruttore dell'istanza che definisci per il tuo tipo.

Ciò ha due implicazioni: in primo luogo, qualsiasi codice di inizializzazione di campo viene duplicato in ciascun costruttore e, in secondo luogo, qualsiasi codice incluso nei costruttori per inizializzare i campi su valori specifici riassegnerà di fatto i campi.

Per quanto riguarda le prestazioni, e per quanto riguarda le dimensioni del codice compilato, è meglio spostare gli inizializzatori di campo in costruttori.

D'altra parte, l'impatto sulle prestazioni e il codice 'bloat' saranno generalmente trascurabili, e la sintassi dell'inizializzatore di campo ha il vantaggio importante di ridurre il rischio che potresti dimenticare di inizializzare un campo in uno dei tuoi costruttori.

Una delle principali limitazioni con gli inizializzatori di campo è che non c'è modo di avvolgerli in un blocco try-finally. Se viene generata un'eccezione in un inizializzatore di campo, qualsiasi risorsa allocata negli inizializzatori precedenti verrà abbandonata; non c'è modo di impedirlo. Altri errori di costruzione possono essere risolti, anche se in modo scomodo, facendo accettare a un costruttore di base protetto un IDisposable come riferimento e indicandolo come la sua prima operazione. Si può quindi evitare di chiamare il costruttore se non attraverso metodi di fabbrica che in caso di eccezione chiameranno Dispose sull'oggetto parzialmente creato. Questa protezione consentirà la pulizia di IDisposable creati in inizializzatori di classe derivata se il costruttore di classe principale fallisce dopo "il contrabbando" un riferimento al nuovo oggetto. 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 con l'inserimento di 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 variabili non inizializzate).

O inizializzerei dove dichiarato. Oppure chiedi al costruttore / ai di chiamare una funzione Init ().

Ad esempio le variabili, è in gran parte una questione di stile (preferisco usare un costruttore). Per le variabili statiche, esiste un vantaggio in termini di prestazioni per inizializzazione in linea (non sempre possibile, ovviamente).

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

Su un punto aggiunto a quanto sopra: hai sempre un costruttore durante l'implementazione di classi che hanno un'implementazione. Se non lo dichiarate, 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 consenti agli inizializzatori di campo per situazioni in cui desideri utilizzare un'implementazione semplificata o predefinita della tua classe / tipo. Questo aggiunge flessibilità al tuo codice. Tieni presente che chiunque può utilizzare l'inizializzatore di campi predefinito se lo desidera ... assicurati di dichiararlo manualmente se offri più di un costruttore - public Foo () {}

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top