2009-07-21 15 views
8

Ho una classe con un campo che deve essere inizializzato quando l'oggetto è inizializzato, come un elenco che deve essere creato prima che gli oggetti possano essere aggiunti/rimossi da esso .Inizializzazione dei campi di classe nella definizione campo o nel costruttore di classe

public class MyClass1 
{ 
    private List<MyOtherClass> _otherClassList; 

    public MyClass1() 
    { 
     this._otherClasslist = new List<MyOtherClass>(); 
    } 
} 


public class MyClass2 
{ 
    private List<MyOtherClass> = new List<MyOtherClass>(); 

    public MyClass2() 
    { 
    } 
} 

Qual è la differenza tra queste due classi e perché scegliere un metodo rispetto all'altro?

Io di solito imposta il campo nel costruttore, come in MyClass1, perché trovo più semplice poter guardare in un punto per vedere tutte le cose che accadono quando l'istanza viene istanziata, ma c'è comunque un caso dove è meglio inizializzare un campo direttamente come in MyClass2?

+0

Hanno già valore di default ... non c'è bisogno di Init di nuovo. –

risposta

5

Gli IL emessi dal compilatore C# (VS2008 sp1) saranno quasi equivalenti per entrambi i casi (anche nelle build Debug e Release).

Tuttavia, se si necessario aggiungere costruttori con parametri che prendonoList<MyOtherClass>come argomento, sarà diverso (in particolare, quando si creerà un significativo numero elevato di oggetti con tali costruttori).

Vedere gli esempi seguenti per vedere le differenze (è possibile copiare & passato a VS e crearlo per vedere IL con Reflector o ILDASM).

using System; 
using System.Collections.Generic; 

namespace Ctors 
{ 
    //Tested with VS2008 SP1 
    class A 
    { 
     //This will be executed before entering any constructor bodies... 
     private List<string> myList = new List<string>(); 

     public A() { } 

     //This will create an unused temp List<string> object 
     //in both Debug and Release build 
     public A(List<string> list) 
     { 
      myList = list; 
     } 
    } 

    class B 
    { 
     private List<string> myList; 

     //ILs emitted by C# compiler are identicial to 
     //those of public A() in both Debug and Release build 
     public B() 
     { 
      myList = new List<string>(); 
     } 

     //No garbage here 
     public B(List<string> list) 
     { 
      myList = list; 
     } 
    } 

    class C 
    { 

     private List<string> myList = null; 
     //In Release build, this is identical to B(), 
     //In Debug build, ILs to initialize myList to null is inserted. 
     //So more ILs than B() in Debug build. 
     public C() 
     { 
      myList = new List<string>(); 
     } 

     //This is identical to B(List<string> list) 
     //in both Debug and Release build. 
     public C(List<string> list) 
     { 
      myList = list; 
     } 
    } 

    class D 
    { 
     //This will be executed before entering a try/catch block 
     //in the default constructor 
     private E myE = new E(); 
     public D() 
     { 
      try 
      { } 
      catch (NotImplementedException e) 
      { 
       //Cannot catch NotImplementedException thrown by E(). 
       Console.WriteLine("Can I catch here???"); 
      } 
     } 
    } 

    public class E 
    { 
     public E() 
     { 
      throw new NotImplementedException(); 
     } 
    } 

    class Program 
    { 
     static void Main(string[] args) 
     { 
      //This will result in an unhandled exception. 
      //You may want to use try/catch block when constructing D objects. 
      D myD = new D(); 
     } 
    } 
} 

Nota: non ho cambiato alcun flag di ottimizzazione quando si passa alla versione build.

0

I codici sono equivalenti perché il compilatore inserisce l'inizializzazione in ogni costruttore che hai nel secondo caso, il vantaggio del secondo caso è che il programmatore non penserà di inizializzare i campi quando aggiungerà un nuovo costruttore dopo l'anno di scrittura di quella classe :)

+0

Lo sviluppatore che inizialmente crea la classe dovrebbe utilizzare il concatenamento del costruttore appropriato, quindi non dovrebbe essere un problema. Tuttavia, il programmatore che modifica la classe un anno dopo, comprende meglio la classe prima che la tocchi. Dovrebbe assicurarsi che tutti i campi vengano inizializzati correttamente. –

0

In C# non c'è praticamente alcuna differenza tra i due esempi. Ma, lo sviluppatore tende a inizializzare i loro campi nel costruttore perché meno incline agli errori.

2

Durante l'inizializzazione in un costruttore, penso che sarebbe più semplice catturare e gestire le eccezioni se necessario.

1

In MyClass1 qualcuno potrebbe ignorare il costruttore e causare problemi.

3

C'è una differenza:

campi sono inizializzati immediatamente prima del costruttore per l'istanza oggetto è chiamato, quindi se il costruttore assegna il valore di un campo , sarà sovrascrivere qualsiasi valore dato durante la dichiarazione di campo. From MSDN:

3

Comportamentalmente, entrambi devono essere identici.
Tuttavia qualcosa che si potrebbe voler considerare è un caso limite IL - Bloat. L'IL per gli inizializzatori di campo è inserito nella parte superiore di ciascun sensore.Ne consegue che se si hanno molti inizializzatori di campo e molti teleruttori sovraccaricati, la stessa sezione di IL è preceduta dal sovraccarico IL. Di conseguenza, la dimensione totale dell'assembly potrebbe aumentare rispetto al caso in cui si utilizza il concatenamento del costruttore o la delega a una funzione comune Initialize() (in cui l'IL ripetuto sarebbe una chiamata al metodo). quindi, per questo specifico scenario, gli inizializzatori di campo sarebbero una scelta relativamente più debole.

Lo si può verificare con riflettore sul binario per questo frammento di codice

public class MyClass2 
    { 
    private List<int> whack = new List<int>(); 
    // lots of other field initializers 

    public MyClass2() 
    { 
     Console.WriteLine("Default ctor"); 
    } 
    public MyClass2(string s) 
    { 
     Console.WriteLine("String overload"); 
    } 
     // lots of other overload ctors 
    } 
Problemi correlati