Riktlinjer för samlingar
Kommentar
Det här innehållet skrivs om med behörighet från Pearson Education, Inc. från Framework Design Guidelines: Conventions, Idioms, and Patterns for Reusable .NET Libraries, 2nd Edition. Den utgåvan publicerades 2008, och boken har sedan dess reviderats helt i den tredje utgåvan. En del av informationen på den här sidan kan vara inaktuell.
Alla typer som utformats specifikt för att manipulera en grupp objekt med någon gemensam egenskap kan betraktas som en samling. Det är nästan alltid lämpligt för sådana typer att implementera IEnumerable eller IEnumerable<T>, så i det här avsnittet överväger vi bara att typer som implementerar ett eller båda av dessa gränssnitt är samlingar.
❌ Använd INTE svagt skrivna samlingar i offentliga API:er.
Typen av alla returvärden och parametrar som representerar samlingsobjekt ska vara den exakta objekttypen, inte någon av dess bastyper (detta gäller endast för offentliga medlemmar i samlingen).
❌ ANVÄND ArrayList INTE eller List<T> i offentliga API:er.
Dessa typer är datastrukturer som är utformade för att användas i intern implementering, inte i offentliga API:er. List<T>
är optimerad för prestanda och kraft på bekostnad av renhet av API:er och flexibilitet. Om du till exempel returnerar List<T>
kommer du aldrig att kunna ta emot meddelanden när klientkoden ändrar samlingen. List<T>
Dessutom exponerar många medlemmar, till exempel BinarySearch, som inte är användbara eller tillämpliga i många scenarier. I följande två avsnitt beskrivs typer (abstraktioner) som utformats specifikt för användning i offentliga API:er.
❌ ANVÄND Hashtable
INTE eller Dictionary<TKey,TValue>
i offentliga API:er.
Dessa typer är datastrukturer som är utformade för att användas i intern implementering. Offentliga API:er ska använda IDictionary, IDictionary <TKey, TValue>
eller en anpassad typ som implementerar ett eller båda gränssnitten.
❌ Använd IEnumerator<T>INTE , IEnumeratoreller någon annan typ som implementerar något av dessa gränssnitt, förutom som returtyp för en GetEnumerator
metod.
Typer som returnerar uppräknare från andra metoder än GetEnumerator
vad som inte kan användas med -instruktionen foreach
.
❌ Implementera INTE både IEnumerator<T>
och IEnumerable<T>
på samma typ. Samma sak gäller för de icke-generiska gränssnitten IEnumerator
och IEnumerable
.
Samlingsparametrar
✔️ Använd den minsta möjliga typen som parametertyp. De flesta medlemmar som tar samlingar som parametrar använder IEnumerable<T>
gränssnittet.
❌ UNDVIK att använda ICollection<T> eller ICollection som en parameter bara för att få åtkomst till egenskapen Count
.
Överväg i stället att använda IEnumerable<T>
eller IEnumerable
dynamiskt kontrollera om objektet implementerar ICollection<T>
eller ICollection
.
Samlingsegenskaper och returvärden
❌ Ange INTE egenskaper för inställbar samling.
Användare kan ersätta innehållet i samlingen genom att rensa samlingen först och sedan lägga till det nya innehållet. Om det är vanligt att ersätta hela samlingen kan du överväga att tillhandahålla AddRange
metoden för samlingen.
✔️ Använd Collection<T>
eller en underklass för Collection<T>
för egenskaper eller returvärden som representerar läs-/skrivsamlingar.
Om Collection<T>
inte uppfyller vissa krav (t.ex. får samlingen inte implementera IList), använd en anpassad samling genom att implementera IEnumerable<T>
, ICollection<T>
eller IList<T>.
✔️ ANVÄND ReadOnlyCollection<T>, en underklass av ReadOnlyCollection<T>
, eller i sällsynta fall IEnumerable<T>
för egenskaper eller returvärden som representerar skrivskyddade samlingar.
I allmänhet föredrar du ReadOnlyCollection<T>
. Om den inte uppfyller vissa krav (t.ex. får samlingen inte implementera IList
), använder du en anpassad samling genom att implementera IEnumerable<T>
, ICollection<T>
eller IList<T>
. Om du implementerar en anpassad skrivskyddad samling implementerar ICollection<T>.IsReadOnly
du för att returnera true
.
I de fall där du är säker på att det enda scenario som du någonsin vill stödja är iteration endast framåt kan du helt enkelt använda IEnumerable<T>
.
✔️ ÖVERVÄG att använda underklasser av generiska bassamlingar i stället för att använda samlingarna direkt.
Detta ger ett bättre namn och för att lägga till hjälpmedlemmar som inte finns i bassamlingstyperna. Detta gäller särskilt för API:er på hög nivå.
✔️ ÖVERVÄG att returnera en underklass av Collection<T>
eller ReadOnlyCollection<T>
från mycket vanliga metoder och egenskaper.
Detta gör det möjligt för dig att lägga till hjälpmetoder eller ändra insamlingsimplementeringen i framtiden.
✔️ ÖVERVÄG att använda en nyckelsamling om objekten som lagras i samlingen har unika nycklar (namn, ID:t osv.). Nyckelade samlingar är samlingar som kan indexeras av både ett heltal och en nyckel och som vanligtvis implementeras genom att ärva från KeyedCollection<TKey,TItem>
.
Nyckelade samlingar har vanligtvis större minnesfotavtryck och bör inte användas om minneskostnaderna uppväger fördelarna med att ha nycklarna.
❌ Returnera INTE null-värden från samlingsegenskaper eller från metoder som returnerar samlingar. Returnera en tom samling eller en tom matris i stället.
Den allmänna regeln är att null- och tomma samlingar eller matriser (0 objekt) ska behandlas på samma sätt.
Ögonblicksbilder kontra livesamlingar
Samlingar som representerar ett tillstånd någon gång kallas ögonblicksbildsamlingar. Till exempel skulle en samling som innehåller rader som returneras från en databasfråga vara en ögonblicksbild. Samlingar som alltid representerar det aktuella tillståndet kallas livesamlingar. En samling ComboBox
objekt är till exempel en live-samling.
❌ Returnera INTE ögonblicksbildsamlingar från egenskaper. Egenskaper bör returnera livesamlingar.
Egenskapsmottagare bör vara mycket enkla åtgärder. För att returnera en ögonblicksbild måste du skapa en kopia av en intern samling i en O(n)-åtgärd.
✔️ Använd antingen en ögonblicksbildssamling eller en live IEnumerable<T>
-samling (eller dess undertyp) för att representera samlingar som är flyktiga (dvs. som kan ändras utan att uttryckligen ändra samlingen).
I allmänhet är alla samlingar som representerar en delad resurs (t.ex. filer i en katalog) instabila. Sådana samlingar är mycket svåra eller omöjliga att implementera som live-samlingar om inte implementeringen bara är en framåtriktad uppräknare.
Välja mellan matriser och samlingar
✔️ FÖREDRAR samlingar framför matriser.
Samlingar ger mer kontroll över innehållet, kan utvecklas över tid och är mer användbara. Dessutom rekommenderas inte att använda matriser för skrivskyddade scenarier eftersom kostnaden för att klona matrisen är oöverkomlig. Användbarhetsstudier har visat att vissa utvecklare känner sig mer bekväma med samlingsbaserade API:er.
Men om du utvecklar API:er på låg nivå kan det vara bättre att använda matriser för läs- och skrivscenarier. Matriser har ett mindre minnesavtryck, vilket hjälper till att minska arbetsuppsättningen, och åtkomsten till element i en matris går snabbare eftersom den optimeras av körningen.
✔️ ÖVERVÄG att använda matriser i API:er på låg nivå för att minimera minnesförbrukningen och maximera prestanda.
✔️ Använd bytematriser i stället för samlingar med byte.
❌ Använd INTE matriser för egenskaper om egenskapen skulle behöva returnera en ny matris (t.ex. en kopia av en intern matris) varje gång egenskapsmottagaren anropas.
Implementera anpassade samlingar
✔️ ÖVERVÄG att ärva från Collection<T>
, ReadOnlyCollection<T>
eller KeyedCollection<TKey,TItem>
när du utformar nya samlingar.
✔️ IMPLEMENTERA IEnumerable<T>
NÄR du utformar nya samlingar. Överväg att implementera ICollection<T>
eller till och med IList<T>
var det är meningsfullt.
När du implementerar en sådan anpassad samling följer du API-mönstret som upprättats av Collection<T>
och ReadOnlyCollection<T>
så nära som möjligt. Det vill: implementera samma medlemmar explicit, namnge parametrarna som dessa två samlingar och så vidare.
✔️ ÖVERVÄG att implementera icke-generiska samlingsgränssnitt (IList
och ICollection
) om samlingen ofta skickas till API:er som tar dessa gränssnitt som indata.
❌ UNDVIK att implementera samlingsgränssnitt på typer med komplexa API:er som inte är relaterade till begreppet samling.
❌ ÄRVER INTE från icke-generiska bassamlingar, till exempel CollectionBase
. Använd Collection<T>
, ReadOnlyCollection<T>
och KeyedCollection<TKey,TItem>
i stället.
Namnge anpassade samlingar
Samlingar (typer som implementerar IEnumerable
) skapas huvudsakligen av två skäl: (1) för att skapa en ny datastruktur med strukturspecifika åtgärder och ofta olika prestandaegenskaper än befintliga datastrukturer (t.ex. List<T>, , LinkedList<T>, Stack<T>) och (2) för att skapa en specialiserad samling för att hålla en specifik uppsättning objekt (t.ex. StringCollection). Datastrukturer används oftast i den interna implementeringen av program och bibliotek. Specialiserade samlingar ska huvudsakligen exponeras i API:er (som egenskaps- och parametertyper).
✔️ ANVÄND suffixet "Ordlista" i namn på abstraktioner som implementerar IDictionary
eller IDictionary<TKey,TValue>
.
✔️ ANVÄND suffixet "Samling" i namn på typer som implementerar IEnumerable
(eller någon av dess underordnade) och representerar en lista med objekt.
✔️ Använd lämpligt datastrukturnamn för anpassade datastrukturer.
❌ UNDVIK att använda suffix som innebär en viss implementering, till exempel "LinkedList" eller "Hashtable", i namn på samlingsabstraktioner.
✔️ ÖVERVÄG att prefixera samlingsnamn med namnet på objekttypen. En samling som lagrar objekt av typen Address
(implementering IEnumerable<Address>
) bör till exempel ha namnet AddressCollection
. Om objekttypen är ett gränssnitt kan "I"-prefixet för objekttypen utelämnas. Därför kan en samling IDisposable objekt kallas DisposableCollection
.
✔️ ÖVERVÄG att använda prefixet "ReadOnly" i namnen på skrivskyddade samlingar om en motsvarande skrivbar samling kan läggas till eller redan finns i ramverket.
Till exempel ska en skrivskyddad samling strängar anropas ReadOnlyStringCollection
.
Portioner © 2005, 2009 Microsoft Corporation. Med ensamrätt.
Reprinted by permission of Pearson Education, Inc. from Framework Design Guidelines: Conventions, Idioms, and Patterns for Reusable .NET Libraries, 2nd Edition by Krzysztof Cwalina and Brad Abrams, publicerad 22 okt 2008 av Addison-Wesley Professional som en del av Microsoft Windows Development Series.