2009-12-12 6 views
92

Supponiamo di avere una classe Persona:È meglio utilizzare Enumerable.Empty <T>() in opposizione al nuovo Elenco <T>() per inizializzare un IEnumerable <T>?

public class Person 
{ 
    public string Name { get; set;} 
    public IEnumerable<Role> Roles {get; set;} 
} 

dovrei ovviamente istanziare i ruoli nel costruttore. Ora, ho usato per farlo con una lista come questa:

public Person() 
{ 
    Roles = new List<Role>(); 
} 

Ma ho scoperto questo metodo statico nella System.Linq namespace

IEnumerable<T> Enumerable.Empty<T>(); 

Da MSDN:

Il Il metodo Empty(TResult)() memorizza nella cache una sequenza vuota di tipo TResult. Quando l'oggetto restituito viene enumerato, non produce elementi.

In alcuni casi, questo metodo è utile per passare una sequenza vuota a un metodo definito dall'utente che prende un IEnumerable(T). Può anche essere utilizzato per generare un elemento neutro per i metodi come Union. Vedere la sezione Esempio per un esempio di questo uso di

Quindi è meglio scrivere il costruttore in questo modo? Lo usi? Perché? o se no, perché no?

public Person() 
{ 
    Roles = Enumerable.Empty<Role>(); 
} 
+0

Sei sicuro di voler un (pubblico) sorge nella proprietà ruoli? –

+0

Questa è una classe Data. Intendo utilizzare questa classe come classe Model quando si implementa un modello di repository con Entity Framework 4.0 (giocando intorno ...). quindi penso che sia bello avere un setter pubblico qui, non è vero? –

+1

serbech, come si aggiungerà un ruolo (all'interno della persona) quando un altro codice può installare qualsiasi tipo di classe derivata IEnumerable per l'elenco? A cosa lo lanci? –

risposta

89

penso che la maggior parte delle registrazioni mancato il punto principale. Anche se si utilizza un array vuoto o una lista vuota, questi sono oggetti e sono archiviati in memoria. Than Garbage Collector deve prendersi cura di loro. Se hai a che fare con applicazioni ad alto throughput, potrebbe essere un impatto notevole.

Enumerable.Empty non crea un oggetto per chiamata, quindi mette meno carico su GC.

Se il codice si trova in una posizione a basso throughput, quindi si riduce a considerazioni estetiche però.

+8

E sta anche riflettendo meglio l'intenzione. –

71

Credo Enumerable.Empty<T> è meglio perché è più esplicito: il codice indica chiaramente le vostre intenzioni. Potrebbe anche essere un po 'più efficiente, ma questo è solo un vantaggio secondario.

+6

Sì, mettendo in chiaro che vuoi dire che questo è vuoto rispetto a qualcosa che potrebbe essere aggiunto in seguito fa bene al tuo codice. –

+21

Sì. Enumerable <>. Empty evita di allocare un nuovo oggetto, che è un piccolo bonus. –

+7

sidenote @NeilWhitaker: è Enumerable.Empty non Enumerable .Empty (che mi ha fatto impazzire una volta ...) – santa

4

Il problema più grande sarebbe quello di esporre Roles come campo pubblico .

il seguente aspetto migliore:

public class Person 
{ 
    public string Name { get; set; } 

    private List<Role> _roles = null; 
    public IEnumerable<Role> Roles 
    { 
     get { 
     if (_roles != null) 
      return _roles; 
     else 
      return Enumerable.Empty<Role>(); 
     } 
    } 
} 

E forse si dovrebbe dare un'occhiata a tornare come un ReadonlyCollection, a seconda di come si desidera utilizzarlo.

E Enumerable.Empty non è better qui, solo un po 'più efficiente quando i ruoli di solito rimane vuoto.

+3

Non sono d'accordo con l'affermazione generale che i campi pubblici sono cattivi. Questo non è il posto giusto per partecipare a quel dibattito, comunque. Basti dire che hai preso 1 riga di testo e l'hai trasformata in 10, senza fornire una risposta utile alla domanda dell'OP. –

+0

Ma dal momento che si tratta di una classe di dati e voglio essere in grado di aggiungere o rimuovere i ruoli di fatto, e quindi salvarli nuovamente nel database, quindi si trasforma in un IList .. Grazie per le risposte, che ha risolto il mio caso Penso :) –

6

Il problema con il vostro approccio è che non è possibile aggiungere tutti gli elementi per la raccolta - vorrei avere una struttura privata come lista e poi esporre gli oggetti come un Enumerable:

public class Person 
{ 
    private IList<Role> _roles; 

    public Person() 
    { 
     this._roles = new List<Role>(); 
    } 

    public string Name { get; set; } 

    public void AddRole(Role role) 
    { 
     //implementation 
    } 

    public IEnumerable<Role> Roles 
    { 
     get { return this._roles.AsEnumerable(); } 
    } 
} 

Se si intende un po ' l'altra classe per creare l'elenco dei ruoli (che non consiglierei) quindi non inizializzerei affatto l'enumerabile in Person.

+0

questo è un buon punto. Lo avrei scoperto rapidamente nella mia implementazione in effetti :) –

+0

Vorrei rendere _roles in sola lettura, e non vedo la necessità della chiamata a AsEnumerable(). Altrimenti, +1 –

+1

@Kent: concordo con readonly. Per quanto riguarda AsEnumerable, non è realmente necessario ma impedisce ai ruoli di essere trasmessi a IList e modificati. – Lee

12

si Supponendo che in realtà desidera popolare alla proprietà Roles in qualche modo, poi incapsulare che facendo è setter privata e l'inizializzazione a un nuovo elenco nel costruttore:

public class Person 
{ 
    public string Name { get; set; } 
    public IList<Role> Roles { get; private set; } 

    public Person() 
    { 
     Roles = new List<Role>(); 
    } 
} 

Se davvero si vuole veramente avere il pubblico setter, lasciare Roles con un valore di null ed evitare l'allocazione dell'oggetto.

5

Il tipico problema con l'esposizione dell'elenco privato come un oggetto IEnumerable è che il client della classe può interferire con esso mediante il cast. Questo codice avrebbe funzionato:

var p = new Person(); 
    List<Role> roles = p.Roles as List<Role>; 
    roles.Add(Role.Admin); 

È possibile evitare questo mediante l'attuazione di un iteratore:

public IEnumerable<Role> Roles { 
    get { 
    foreach (var role in mRoles) 
     yield return role; 
    } 
} 
+3

Se uno sviluppatore inserisce il tuo IEnumerable in un elenco , allora questo è il suo problema e non il tuo. Se cambi l'implementazione interna in futuro (quindi non è più List ), non è colpa tua se il codice dello sviluppatore si rompe. Non puoi impedire alle persone di scrivere codice scadente e abusare della tua API, quindi non penso che valga la pena di dedicare molto impegno. –

+2

Non si tratta di rompere il codice, si tratta di sfruttare il codice. –

+0

Sono d'accordo con @Hans se si tratta di un problema di sicurezza e con @Tommy in tutti gli altri casi. Gli sviluppatori cattivi fanno sempre un errore fatale. – TrueWill

11

Sul fronte delle prestazioni, vediamo come è implementato Enumerable.Empty<T>.

Esso restituisce EmptyEnumerable<T>.Instance, che è definito come:

internal class EmptyEnumerable<T> 
{ 
    public static readonly T[] Instance = new T[0]; 
} 

campi statici sui tipi generici sono assegnati per parametro di tipo generico. Ciò significa che il runtime può creare pigramente questi array vuoti solo per i tipi richiesti dal codice utente e riutilizzare le istanze tutte le volte necessarie senza aggiungere alcuna pressione sul garbage collector.

Vale a dire:

Debug.Assert(ReferenceEquals(Enumerable.Empty<int>(), Enumerable.Empty<int>())); 
Problemi correlati