Domanda

Prima dei generici C#, tutti codificavano le raccolte per i propri oggetti aziendali creando una base di raccolta che implementava IEnumerable

CIOÈ:

public class CollectionBase : IEnumerable

e da ciò deriverebbero le raccolte di oggetti Business.

public class BusinessObjectCollection : CollectionBase

Ora, con la classe elenco generica, qualcuno la usa invece?Ho scoperto che utilizzo un compromesso tra le due tecniche:

public class BusinessObjectCollection : List<BusinessObject>

Lo faccio perché mi piace avere nomi fortemente digitati invece di limitarmi a passare liste in giro.

Cosa è tuo approccio?

È stato utile?

Soluzione

In genere sono propenso a utilizzare direttamente un List, a meno che per qualche motivo non sia necessario incapsulare la struttura dei dati e fornire un sottoinsieme limitato delle sue funzionalità.Ciò è dovuto principalmente al fatto che se non ho un'esigenza specifica di incapsulamento, farlo è solo una perdita di tempo.

Tuttavia, con la funzionalità di inizializzazione aggregata in C# 3.0, esistono alcune nuove situazioni in cui consiglierei l'uso di classi di raccolta personalizzate.

Fondamentalmente, C# 3.0 consente qualsiasi classe che implementi IEnumerable e dispone di un metodo Add per utilizzare la nuova sintassi dell'inizializzatore aggregato.Ad esempio, poiché Dictionary definisce un metodo Add(chiave K, valore V), è possibile inizializzare un dizionario utilizzando questa sintassi:

var d = new Dictionary<string, int>
{
    {"hello", 0},
    {"the answer to life the universe and everything is:", 42}
};

La cosa bella di questa funzionalità è che funziona per aggiungere metodi con qualsiasi numero di argomenti.Ad esempio, data questa raccolta:

class c1 : IEnumerable
{
    void Add(int x1, int x2, int x3)
    {
        //...
    }

    //...
}

sarebbe possibile inizializzarlo in questo modo:

var x = new c1
{
    {1,2,3},
    {4,5,6}
}

Questo può essere davvero utile se hai bisogno di creare tabelle statiche di oggetti complessi.Ad esempio, se stavi solo usando List<Customer> e volessi creare un elenco statico di oggetti cliente, dovresti crearlo in questo modo:

var x = new List<Customer>
{
    new Customer("Scott Wisniewski", "555-555-5555", "Seattle", "WA"),
    new Customer("John Doe", "555-555-1234", "Los Angeles", "CA"),
    new Customer("Michael Scott", "555-555-8769", "Scranton PA"),
    new Customer("Ali G", "", "Staines", "UK")
}

Tuttavia, se utilizzi una raccolta personalizzata, come questa:

class CustomerList  : List<Customer>
{
    public void Add(string name, string phoneNumber, string city, string stateOrCountry)
    {
        Add(new Customer(name, phoneNumber, city, stateOrCounter));
    }
}

Potresti quindi inizializzare la raccolta utilizzando questa sintassi:

var customers = new CustomerList
{
    {"Scott Wisniewski", "555-555-5555", "Seattle", "WA"},
    {"John Doe", "555-555-1234", "Los Angeles", "CA"},
    {"Michael Scott", "555-555-8769", "Scranton PA"},
    {"Ali G", "", "Staines", "UK"}
}

Ciò ha il vantaggio di essere più facile da digitare e più facile da leggere perché non è necessario ridigitare il nome del tipo di elemento per ciascun elemento.Il vantaggio può essere particolarmente forte se il tipo di elemento è lungo o complesso.

Detto questo, è utile solo se hai bisogno di raccolte statiche di dati definite nella tua app.Alcuni tipi di app, come i compilatori, li usano continuamente.Altri, come le tipiche app di database, non lo fanno perché caricano tutti i dati da un database.

Il mio consiglio sarebbe che se hai bisogno di definire una raccolta statica di oggetti o di incapsulare l'interfaccia della raccolta, crea una classe di raccolta personalizzata.Altrimenti userei semplicemente List<T> direttamente.

Altri suggerimenti

Suo consigliato che nelle API pubbliche non utilizzare List<T>, ma utilizzare Collection<T>

Se però ne stai ereditando, dovresti stare bene, afaik.

Preferisco semplicemente usare List<BusinessObject>.La definizione della digitazione aggiunge semplicemente un boilerplate non necessario al codice. List<BusinessObject> è un tipo specifico, non è uno qualsiasi List oggetto, quindi è ancora fortemente tipizzato.

Ancora più importante, dichiarare qualcosa List<BusinessObject> rende più semplice per tutti coloro che leggono il codice capire con quali tipi hanno a che fare, non devono cercare per capire di cosa si tratta BusinessObjectCollection is e poi ricorda che è solo un elenco.Con la typedefing, dovrai richiedere una convenzione di (ri)denominazione coerente che tutti devono seguire affinché abbia senso.

Usa il tipo List<BusinessObject> dove devi dichiararne un elenco.Tuttavia, dove restituisci un elenco di BusinessObject, valuta la possibilità di tornare IEnumerable<T>, IList<T> O ReadOnlyCollection<T> - cioè.restituire il contratto più debole possibile che soddisfi il cliente.

Laddove desideri "aggiungere codice personalizzato" a un elenco, metodi di estensione del codice nel tipo di elenco.Ancora una volta, collega questi metodi al contratto più debole possibile, ad es.

public static int SomeCount(this IEnumerable<BusinessObject> someList)

Naturalmente, non puoi e non dovresti aggiungere lo stato con metodi di estensione, quindi se devi aggiungere una nuova proprietà e un campo dietro di essa, usa una sottoclasse o, meglio, una classe wrapper per memorizzarla.

Vado avanti e indietro su 2 opzioni:

public class BusinessObjectCollection : List<BusinessObject> {}

o metodi che eseguono semplicemente le seguenti operazioni:

public IEnumerable<BusinessObject> GetBusinessObjects();

Il vantaggio del primo approccio è che puoi modificare l'archivio dati sottostante senza dover interferire con le firme dei metodi.Sfortunatamente se erediti da un tipo di raccolta che rimuove un metodo dall'implementazione precedente, dovrai gestire queste situazioni in tutto il codice.

Probabilmente dovresti evitare di creare la tua collezione per quello scopo.È abbastanza comune voler modificare il tipo di struttura dei dati alcune volte durante i refactoring o quando si aggiungono nuove funzionalità.Con il tuo approccio, ti ritroveresti con una classe separata per BusinessObjectList, BusinessObjectDictionary, BusinessObjectTree, ecc.

Non vedo alcun valore nella creazione di questa classe solo perché il nome della classe è più leggibile.Sì, la sintassi delle parentesi angolari è un po' brutta, ma è standard in C++, C# e Java, quindi anche se non scrivi codice che la utilizza, la incontrerai continuamente.

Generalmente derivano le mie classi di raccolta solo se ho bisogno di "aggiungere valore".Ad esempio, se la raccolta stessa avesse bisogno di avere alcune proprietà "metadati" taggate insieme ad essa.

Faccio esattamente la stessa cosa che fai tu, Jonathan...eredita semplicemente da List<T>.Ottieni il meglio da entrambi i mondi.Ma generalmente lo faccio solo quando c'è qualche valore da aggiungere, come aggiungere a LoadAll() metodo o altro.

Puoi usarli entrambi.Per la pigrizia - intendo la produttività - List è un corso molto utile, è anche "esauriente" e francamente pieno di membri YANGNI.Insieme all'argomentazione/raccomandazione sensata avanzata dal Articolo MSDN già linkato sull'esposizione di List come membro pubblico, preferisco il "terzo" modo:

Personalmente utilizzo il pattern decoratore per esporre solo ciò che mi serve da List, ovvero:

public OrderItemCollection : IEnumerable<OrderItem> 
{
    private readonly List<OrderItem> _orderItems = new List<OrderItem>();

    void Add(OrderItem item)
    {
         _orderItems.Add(item)
    }

    //implement only the list members, which are required from your domain. 
    //ie. sum items, calculate weight etc...

    private IEnumerator<string> Enumerator() {
        return _orderItems.GetEnumerator();
    }

    public IEnumerator<string> GetEnumerator() {
        return Enumerator();
    }    
}

Inoltre probabilmente astrarrei OrderItemCollection in IOrderItemCollection in modo da poter scambiare la mia implementazione di IOrderItemCollection in futuro (potrei preferire utilizzare un diverso oggetto enumerabile interno come Collection o più probabilmente per utilizzare perf una raccolta o un set di coppie di valori chiave .

Utilizzo elenchi generici per quasi tutti gli scenari.L'unica volta in cui prenderei in considerazione l'utilizzo di una raccolta derivata è se aggiungo membri specifici della raccolta.Tuttavia, l’avvento di LINQ ne ha ridotto la necessità.

6 di 1, mezza dozzina di un altro

In ogni caso è la stessa cosa.Lo faccio solo quando ho motivo di aggiungere codice personalizzato in BusinessObjectCollection.

Senza che i metodi di caricamento restituiscano un elenco mi consente di scrivere più codice in una classe generica comune e farlo funzionare.Come un metodo Load.

Come ha sottolineato qualcun altro, si consiglia di non esporre pubblicamente List e FxCop si lamenterà se lo fai.Ciò include l'ereditarietà da List come in:

public MyTypeCollection : List<MyType>

Nella maggior parte dei casi le API pubbliche esporranno IList (o ICollection o IEnumerable) a seconda dei casi.

Nei casi in cui desideri la tua raccolta personalizzata, puoi mantenere FxCop silenzioso ereditando da Collection anziché da List.

Se scegli di creare la tua classe di raccolta, dovresti controllare i tipi in Spazio dei nomi System.Collections.ObjectModel.

Lo spazio dei nomi definisce le classi base pensate per rendere più semplice per gli implementatori la creazione di raccolte personalizzate.

Tendo a farlo con la mia collezione se voglio proteggere l'accesso all'elenco vero e proprio.Quando scrivi oggetti business, è probabile che tu abbia bisogno di un hook per sapere se il tuo oggetto viene aggiunto/rimosso, in questo senso penso che BOCollection sia un'idea migliore.Ovviamente se ciò non è richiesto, List è più leggero.Inoltre potresti voler controllare l'utilizzo di IList per fornire un'interfaccia di astrazione aggiuntiva se hai bisogno di qualche tipo di proxy (ad es.una raccolta falsa attiva il caricamento lento dal database)

Ma...perché non prendere in considerazione Castle ActiveRecord o qualsiasi altro framework ORM maturo?:)

Nella maggior parte dei casi scelgo semplicemente il metodo List, poiché mi offre tutte le funzionalità di cui ho bisogno nel 90% dei casi e quando è necessario qualcosa di "extra", ne eredito e codifico quel bit in più .

farei questo:

using BusinessObjectCollection = List<BusinessObject>;

Questo crea semplicemente un alias anziché un tipo completamente nuovo.Lo preferisco all'utilizzo diretto di List<BusinessObject> perché mi lascia libero di modificare la struttura sottostante della raccolta in futuro senza modificare il codice che lo utilizza (purché fornisca le stesse proprietà e metodi).

prova questo:

System.Collections.ObjectModel.Collection<BusinessObject>

non è necessario implementare il metodo di base come fa CollectionBase

questo è il modo:

restituisci array, accetta IEnumerable<T>

=)

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