Přehled obecných typů
Vývojáři používají obecné typy po celou dobu v .NET bez ohledu na to, jestli implicitně nebo explicitně. Když používáte LINQ v .NET, všimli jste si, že pracujete IEnumerable<T>? Nebo pokud jste někdy viděli online ukázku "obecného úložiště" pro komunikaci s databázemi pomocí Entity Framework, viděli jste, že většina metod vrací IQueryable<T>
? Možná jste se divili, co je T v těchto příkladech a proč tam je.
Poprvé představené v rozhraní .NET Framework 2.0 jsou obecné typy v podstatě "šablona kódu", která vývojářům umožňuje definovat datové struktury bezpečné pro typ, aniž by se zavázali k skutečnému datovému typu. Například je obecná kolekce, List<T> která lze deklarovat a používat s jakýmkoli typem, například List<int>
, List<string>
nebo List<Person>
.
Abychom pochopili, proč jsou obecné typy užitečné, podívejme se na konkrétní třídu před a po přidání obecných typů: ArrayList. V rozhraní .NET Framework 1.0 ArrayList
byly prvky typu Object. Jakýkoli prvek přidaný do kolekce byl bezobslužně převeden na .Object
Totéž by se stalo při čtení prvků ze seznamu. Tento proces se označuje jako boxování a rozbalování a má vliv na výkon. Kromě výkonu ale neexistuje způsob, jak určit typ dat v seznamu v době kompilace, což dělá pro nějaký křehký kód. Obecné typy řeší tento problém definováním typu dat, která bude každá instance seznamu obsahovat. Můžete například přidávat pouze celá čísla a List<int>
přidávat pouze osoby do List<Person>
.
Obecné typy jsou k dispozici také za běhu. Modul runtime ví, jaký typ datové struktury používáte, a může ho uložit do paměti efektivněji.
Následující příklad je malý program, který ilustruje efektivitu znalosti datové struktury typu za běhu:
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();
}
}
}
Tento program vytvoří výstup podobný následujícímu:
Generic Sort: System.Collections.Generic.List`1[System.Int32]
Time taken: 0.0034ms
Non-Generic Sort: System.Collections.ArrayList
Time taken: 0.2592ms
První věc, kterou si zde můžete všimnout, je, že řazení obecného seznamu je výrazně rychlejší než řazení ne generického seznamu. Můžete si také všimnout, že typ obecného seznamu je odlišný ([System.Int32]), zatímco typ pro negenerický seznam je zobecněn. Vzhledem k tomu, že modul runtime ví, že obecný List<int>
typ je , Int32může ukládat prvky seznamu do podkladového celočíselného pole v paměti, zatímco non-generic ArrayList
musí přetypovat každý prvek seznamu na objekt. Jak ukazuje tento příklad, přetypování navíc zabere čas a zpomalí řazení seznamu.
Další výhodou modulu runtime s vědomím typu vašeho obecného prostředí je lepší prostředí ladění. Při ladění obecného v jazyce C# víte, jaký typ má každý prvek ve své datové struktuře. Bez obecných typů byste neměli ponětí, jaký typ každý prvek byl.