Domanda

Mi chiedo quale di questi sarebbe considerato il più pulito o il migliore da usare e perché.

Uno di questi espone un elenco di passeggeri, che consente all'utente di aggiungere e rimuovere ecc. L'altro nasconde l'elenco e consente solo all'utente di enumerarli e aggiungerli utilizzando un metodo speciale.

Esempio 1

class Bus
{
    public IEnumerable<Person> Passengers { get { return passengers; } }
    private List<Passengers> passengers;

    public Bus()
    {
        passengers = new List<Passenger>();
    }

    public void AddPassenger(Passenger passenger)
    {
        passengers.Add(passenger);
    }
}

var bus = new Bus1();
bus.AddPassenger(new Passenger());
foreach(var passenger in bus.Passengers)
    Console.WriteLine(passenger);

Esempio 2

class Bus
{
    public List<Person> Passengers { get; private set; }

    public Bus()
    {
        Passengers = new List<Passenger>();
    }
}

var bus = new Bus();
bus.Passengers.Add(new Passenger());
foreach(var passenger in bus.Passengers)
    Console.WriteLine(passenger);

La prima classe, direi, è meglio incapsulata. E in questo caso esatto, potrebbe essere l'approccio migliore (poiché probabilmente dovresti assicurarti che rimanga spazio sul bus, ecc.). Ma suppongo che potrebbero esserci casi in cui anche la seconda classe può essere utile? Come se alla classe non importasse davvero cosa succede a quell'elenco purché ne abbia uno. Cosa ne pensi?

È stato utile?

Soluzione

Nell'esempio uno, è possibile mutare la tua collezione.

Considera quanto segue:

var passengers = (List<Passenger>)bus.Passengers;

// Now I have control of the list!
passengers.Add(...);
passengers.Remove(...);

Per risolvere questo problema, potresti considerare qualcosa del genere:

class Bus
{
  private List<Passenger> passengers;

  // Never expose the original collection
  public IEnumerable<Passenger> Passengers
  {
     get { return passengers.Select(p => p); }  
  }

  // Or expose the original collection as read only
  public ReadOnlyCollection<Passenger> ReadOnlyPassengers
  {
     get { return passengers.AsReadOnly(); }
  }

  public void AddPassenger(Passenger passenger)
  {
     passengers.Add(passenger);
  }
 }

Altri suggerimenti

Nella maggior parte dei casi considererei accettabile l'esempio 2 a condizione che il tipo sottostante fosse estensibile e / o esposto una qualche forma di eventi onAdded / onRemoved in modo che la classe interna potesse rispondere a qualsiasi modifica alla raccolta.

In questo caso Elenco < T > non è adatto in quanto non esiste modo per la classe di sapere se è stato aggiunto qualcosa. Invece dovresti usare una Collezione perché la Collezione & Lt; T & Gt; La classe ha diversi membri virtuali (Inserisci, Rimuovi, Imposta, Cancella) che possono essere sovrascritti e trigger di eventi aggiunti per notificare la classe di wrapping.

(Devi anche essere consapevole del fatto che gli utenti della classe possono modificare gli elementi nell'elenco / raccolta senza che la classe genitrice ne sia a conoscenza, quindi assicurati di non fare affidamento sugli elementi invariati, a meno che non sono immutabili ovviamente - oppure puoi fornire eventi di stile modificati se necessario.

Esegui i tuoi rispettivi esempi tramite FxCop e questo dovrebbe darti un suggerimento sui rischi di esporre List<T>

Direi che tutto dipende dalla tua situazione. Normalmente sceglierei l'opzione 2 in quanto è la più semplice, a meno che non abbia un motivo commerciale per aggiungere controlli più rigorosi ad essa.

L'opzione 2 è la più semplice, ma consente ad altre classi di aggiungere / rimuovere elementi alla raccolta, il che può essere pericoloso.

Penso che una buona euristica sia considerare cosa fanno i metodi wrapper. Se il tuo metodo AddPassenger (o Rimuovi o altri) sta semplicemente inoltrando la chiamata alla raccolta, sceglierei la versione più semplice. Se devi controllare gli elementi prima di inserirli, l'opzione 1 è praticamente inevitabile. Se devi tenere traccia degli elementi inseriti / eliminati, puoi procedere in entrambi i modi. Con l'opzione 2 devi registrare gli eventi sulla raccolta per ricevere notifiche e con l'opzione 1 devi creare wrapper per ogni operazione nell'elenco che desideri utilizzare (ad es. Se vuoi inserire e aggiungere), quindi immagino dipende.

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