Általános típusok áttekintése
A fejlesztők folyamatosan általános generikusokat használnak a .NET-ben, akár implicit módon, akár explicit módon. Amikor a LINQ-t a .NET-ben használja, észrevette már, hogy dolgozik?IEnumerable<T> Vagy ha valaha is látott egy online mintát egy "általános adattárból", amely az Entity Framework használatával beszél az adatbázisokkal, látta, hogy a legtöbb metódus visszatér IQueryable<T>
? Lehet, hogy elgondolkodott azon, hogy mi a T ezekben a példákban, és miért van benne.
A .NET-keretrendszer 2.0-s verzióban először bevezetett általános adatok lényegében egy "kódsablon", amely lehetővé teszi a fejlesztők számára, hogy típusbiztos adatstruktúrákat definiáljanak anélkül, hogy tényleges adattípust véglegesítettek volna. Például egy általános gyűjtemény, List<T> amely bármilyen típussal deklarálható és használható, például List<int>
: , List<string>
vagy List<Person>
.
Annak megértéséhez, hogy miért hasznosak az általánosak, vessünk egy pillantást egy adott osztályra az általános adatok hozzáadása előtt és után: ArrayList. A .NET-keretrendszer 1.0-s verziójában az ArrayList
elemek típusa Objectvolt. A gyűjteményhez hozzáadott elemeket a rendszer csendesen átalakította .Object
Ugyanez történik a lista elemeinek olvasásakor is. Ezt a folyamatot boxing és unboxing néven ismerjük, és hatással van a teljesítményre. A teljesítményen kívül azonban nem lehet meghatározni a listában szereplő adatok típusát fordításkor, ami némi törékeny kódot tesz lehetővé. Az általánosak úgy oldják meg ezt a problémát, hogy meghatározzák az egyes listapéldányok által tartalmazott adatok típusát. Például csak egész számokat List<int>
adhat hozzá, és csak Személyek elemet adhat hozzá List<Person>
.
A generikusok futásidőben is elérhetők. A futtatókörnyezet tudja, milyen típusú adatstruktúrát használ, és hatékonyabban tárolhatja a memóriában.
Az alábbi példa egy kis program, amely bemutatja az adatstruktúra típusának futásidőben való megismerésének hatékonyságát:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
namespace GenericsExample {
class Program {
static void Main(string[] args) {
//generic list
List<int> ListGeneric = new List<int> { 5, 9, 1, 4 };
//non-generic list
ArrayList ListNonGeneric = new ArrayList { 5, 9, 1, 4 };
// timer for generic list sort
Stopwatch s = Stopwatch.StartNew();
ListGeneric.Sort();
s.Stop();
Console.WriteLine($"Generic Sort: {ListGeneric} \n Time taken: {s.Elapsed.TotalMilliseconds}ms");
//timer for non-generic list sort
Stopwatch s2 = Stopwatch.StartNew();
ListNonGeneric.Sort();
s2.Stop();
Console.WriteLine($"Non-Generic Sort: {ListNonGeneric} \n Time taken: {s2.Elapsed.TotalMilliseconds}ms");
Console.ReadLine();
}
}
}
Ez a program a következőhöz hasonló kimenetet hoz létre:
Generic Sort: System.Collections.Generic.List`1[System.Int32]
Time taken: 0.0034ms
Non-Generic Sort: System.Collections.ArrayList
Time taken: 0.2592ms
Az első dolog, amit itt láthat, az az, hogy az általános lista rendezése jelentősen gyorsabb, mint a nem általános lista rendezése. Azt is megfigyelheti, hogy az általános lista típusa eltérő ([System.Int32]), míg a nem általános lista típusa általánosított. Mivel a futtatókörnyezet tudja, hogy az általános List<int>
típus Int32, a listaelemeket egy mögöttes egész számtömbben tárolhatja a memóriában, míg a nem általánosaknak ArrayList
minden egyes listaelemet egy objektumhoz kell adniuk. Ahogy ez a példa is mutatja, a további szereplők időt vesznek igénybe, és lelassítják a lista rendezését.
A futtatókörnyezet további előnye, hogy az általános típus ismeretében jobb hibakeresési élmény érhető el. Ha egy általános C#-beli hibakeresést használ, tudja, hogy az egyes elemek milyen típusúak az adatstruktúrában. Általános elemek nélkül fogalma sincs, hogy az egyes elemek milyen típusúak.