Sdílet prostřednictvím


Systém typů jazyka C#

Jazyk C# je jazyk silného typu. Každá proměnná a konstanta má typ, stejně jako každý výraz, který se vyhodnotí jako hodnota. Jazyk C# primárně používá normativní systém typů. Normativní systém typů používá názvy k identifikaci jednotlivých typů. V jazyce C#, struct, class, a interface typech včetně record typů jsou všechny identifikovány jejich názvem. Každá deklarace metody určuje název, typ a druh (hodnota, odkaz nebo výstup) pro každý parametr a návratovou hodnotu. Knihovna tříd .NET definuje předdefinované číselné typy a komplexní typy, které představují širokou škálu konstruktorů. Tyto konstrukce zahrnují systém souborů, síťová připojení, kolekce a pole objektů a kalendářní data. Typický program jazyka C# používá typy z knihovny tříd a uživatelem definovaných typů, které modelují koncepty specifické pro doménu problému programu.

Jazyk C# také podporuje strukturální typy, jako jsou n-tice a anonymní typy. Strukturální typy jsou definovány názvy a typy jednotlivých členů a pořadí členů ve výrazu. Strukturální typy nemají jedinečné názvy.

Informace uložené v typu můžou obsahovat následující položky:

  • Prostor úložiště, který vyžaduje proměnná daného typu.
  • Maximální a minimální hodnoty, které může představovat.
  • Členové (metody, pole, události atd.), které obsahuje.
  • Základní typ, ze něhož dědí.
  • Rozhraní, která implementuje.
  • Povolené operace.

Kompilátor používá informace o typu, aby se zajistilo, že všechny operace prováděné v kódu jsou typu bezpečné. Pokud například deklarujete proměnnou typu int, kompilátor umožňuje použít proměnnou při sčítání a odčítání operací. Pokud se pokusíte provést stejné operace s proměnnou typu bool, kompilátor vygeneruje chybu, jak je znázorněno v následujícím příkladu:

int a = 5;
int b = a + 2; //OK

bool test = true;

// Error. Operator '+' cannot be applied to operands of type 'int' and 'bool'.
int c = a + test;

Poznámka:

Vývojáři C a C++, všimněte si, že v jazyce C# bool není převoditelný na int.

Kompilátor vloží informace o typu do spustitelného souboru jako metadata. Modul CLR (Common Language Runtime) používá tato metadata za běhu k dalšímu zajištění bezpečnosti typů při přidělení a uvolnění paměti.

Určení typů v deklarací proměnných

Když deklarujete proměnnou nebo konstantu v programu, musíte buď zadat jeho typ, nebo použít var klíčové slovo, aby kompilátor odvodil typ. Následující příklad ukazuje některé deklarace proměnných, které používají předdefinované číselné typy i složité uživatelem definované typy:

// Declaration only:
float temperature;
string name;
MyClass myClass;

// Declaration with initializers (four examples):
char firstLetter = 'C';
var limit = 3;
int[] source = [0, 1, 2, 3, 4, 5];
var query = from item in source
            where item <= limit
            select item;

V deklaraci metody zadáte typy parametrů metody a návratové hodnoty. Následující podpis ukazuje metodu, která vyžaduje int jako vstupní argument a vrací řetězec:

public string GetName(int ID)
{
    if (ID < names.Length)
        return names[ID];
    else
        return String.Empty;
}
private string[] names = ["Spencer", "Sally", "Doug"];

Jakmile deklarujete proměnnou, nemůžete ji předefinovat novým typem a nemůžete přiřadit hodnotu, která není kompatibilní s deklarovaným typem. Nemůžete například deklarovat int a pak ji přiřadit logickou hodnotu true. Můžete ale převést hodnoty na jiné typy, například když je přiřadíte k novým proměnným nebo je předáte jako argumenty metody. Kompilátor automaticky provede převod typu , který nezpůsobí ztrátu dat. Převod, který může způsobit ztrátu dat, vyžaduje přetypování ve zdrojovém kódu.

Další informace naleznete v tématu Přetypování a převody typů.

Vestavěné typy

Jazyk C# poskytuje standardní sadu předdefinovaných typů. Tyto typy představují celá čísla, hodnoty s plovoucí desetinnou čárkou, logické výrazy, textové znaky, desetinné hodnoty a další typy dat. Jazyk obsahuje také předdefinované string typy a object typy. Tyto typy můžete použít v libovolném programu jazyka C#. Úplný seznam předdefinovaných typů najdete v tématu Předdefinované typy.

Vlastní typy

Vytvořte strukturální typy pomocí řazených kolekcí členů pro ukládání souvisejících datových členů. Tyto typy poskytují strukturu, která obsahuje více členů. Řazené kolekce členů mají omezené chování. Jedná se o kontejner pro hodnoty. Jedná se o nejjednodušší typy, které můžete vytvořit. Později se můžete rozhodnout, že budete potřebovat chování. V takovém případě můžete převést n-tici na struct nebo na class.

Pomocí konstrukcí struct, class, interface, enum a record vytvořte vlastní typy. Samotná knihovna tříd .NET je kolekce vlastních typů, které můžete použít ve vlastních aplikacích. Ve výchozím nastavení jsou nejčastěji používané typy v knihovně tříd dostupné v libovolném programu jazyka C#. Další typy zpřístupníte tak, že explicitně přidáte odkaz na balíček, který je poskytuje. Jakmile kompilátor odkazuje na balíček, můžete deklarovat proměnné a konstanty typů deklarovaných v sestaveních daného balíčku ve zdrojovém kódu.

Jedním z prvních rozhodnutí, která provedete při definování typu, je rozhodnutí, který konstruktor se má pro váš typ použít. Následující seznam vám pomůže provést toto počáteční rozhodnutí. Některé volby se překrývají. Ve většině scénářů je více než jedna možnost rozumnou volbou.

  • Pokud datový typ není součástí domény vaší aplikace a nezahrnuje chování, použijte strukturální typ.
  • Pokud je velikost úložiště dat malá, maximálně 64 bajtů, zvolte struct nebo record struct.
  • Pokud je typ neměnný nebo chcete nedestruktivní mutaci, zvolte struct nebo record struct.
  • Pokud váš typ by měl mít sémantiku hodnot pro rovnost, zvolte record class nebo record struct.
  • Pokud je typ primárně určen pro ukládání dat s minimálním chováním, zvolte record class nebo record struct.
  • Pokud je typ součástí hierarchie dědičnosti, zvolte položku record class nebo class.
  • Pokud typ používá polymorfismus, zvolte .class
  • Pokud je primárním účelem chování, zvolte class.

Můžete také zvolit interface k modelování kontraktu: chování popsané členy, které mohou být implementovány nesouvisejícími typy. Rozhraní jsou abstraktní a deklarují členy, které musí být implementovány všemi typy class nebo struct, které dědí z tohoto rozhraní.

Systém společných typů

Běžný systém typů podporuje princip dědičnosti. Typy mohou být odvozeny od jiných typů, označovaných jako základní typy. Odvozený typ dědí (s určitými omezeními) metody, vlastnosti a další členy základního typu. Základní typ lze zase odvodit z jiného typu, v takovém případě odvozený typ dědí členy obou základních typů v hierarchii dědičnosti.

Všechny typy, včetně předdefinovaných číselných typů, jako System.Int32 je například (klíčové slovo jazyka C#: int), se nakonec odvozují z jednoho základního typu, což je System.Object (klíčové slovo jazyka C#: object). Tato sjednocená hierarchie typů se nazývá Common Type System (CTS). Další informace o dědičnosti v jazyce C# najdete v tématu Dědičnost.

Každý typ v CTS je definován jako typ hodnoty nebo odkazový typ. Mezi tyto typy patří všechny vlastní typy v knihovně tříd .NET a také vlastní typy definované uživatelem:

  • Typy, které definujete pomocí struct klíčových slov, record struct jsou typy hodnot. Všechny předdefinované číselné typy jsou structs.
  • Typy, které definujete pomocí classrecord class, nebo record klíčových slov jsou odkazové typy.

Odkazové typy a typy hodnot mají různá pravidla kompilace a různé chování za běhu.

Poznámka:

Nejčastěji používané typy jsou všechny uspořádány v System oboru názvů. Obor názvů, ve kterém je typ obsažen, však nemá žádný vztah k tomu, zda se jedná o typ hodnoty nebo typ odkazu.

Třídy a struktury jsou dvěma ze základních konstruktorů systému běžných typů v .NET. Každá konstrukce je v podstatě datová struktura, která zapouzdřuje sadu dat a chování, které patří dohromady jako logická jednotka. Data a chování jsou členy třídy, struktury nebo záznamu. Členové zahrnují své metody, vlastnosti, události a tak dále, jak je uvedeno dále v tomto článku.

Deklarace třídy, struktury nebo záznamu je jako podrobný plán, který používáte k vytváření instancí nebo objektů za běhu. Pokud definujete třídu, strukturu nebo záznam s názvem Person, Person je název typu. Pokud deklarujete a inicializujete proměnnou p typu Person, p se říká, že se jedná o objekt nebo instanci Person. Můžete vytvořit více instancí stejného Person typu a každá instance může mít v jejích vlastnostech a polích různé hodnoty.

Třída je referenční typ. Když vytvoříte objekt typu, proměnná, ke které objekt přiřadíte, bude obsahovat pouze odkaz na tuto paměť. Když přiřadíte odkaz na objekt k nové proměnné, nová proměnná odkazuje na původní objekt. Změny provedené prostřednictvím jedné proměnné se projeví v druhé proměnné, protože obě odkazují na stejná data.

Struktura je typ hodnoty. Když vytvoříte strukturu, proměnná, ke které přiřadíte strukturu, obsahuje skutečná data struktury. Když přiřadíte strukturu nové proměnné, zkopíruje se. Nová proměnná a původní proměnná proto obsahují dvě samostatné kopie stejných dat. Změny provedené v jedné kopii nemají vliv na druhou kopii.

Typy záznamů můžou být odkazové typy (record class) nebo typy hodnot (record struct). Typy záznamů obsahují metody, které podporují rovnost hodnot.

Obecně platí, že třídy slouží k modelování složitějšího chování. Třídy obvykle ukládají data, která upravíte po vytvoření objektu třídy. Struktury jsou nejvhodnější pro malé datové struktury. Struktury obvykle ukládají data, která po vytvoření struktury neupravujete. Typy záznamů jsou datové struktury s dodatečnými syntetizovanými členy kompilátoru. Záznamy obvykle ukládají data, která po vytvoření objektu neupravujete.

Typy hodnot

Typy hodnot jsou odvozeny od System.ValueType, které jsou odvozeny z System.Object. Typy, které jsou odvozeny ze System.ValueType, mají speciální chování v CLR. Proměnné typu hodnoty přímo obsahují jejich hodnoty. Paměť pro strukturu je přidělena přímo v kontextu, kde je proměnná deklarována. Můžete deklarovat record struct typy, které jsou typy hodnot, a zahrnout syntetizované členy pro záznamy.

Existují dvě kategorie hodnotových typů: struct a enum.

Předdefinované číselné typy jsou struktury a mají pole a metody, ke kterým máte přístup:

// constant field on type byte.
byte b = byte.MaxValue;

Deklarujete a přiřadíte jim hodnoty, jako by to byly jednoduché neagregační typy:

byte num = 0xA;
int i = 5;
char c = 'Z';

Typy hodnot jsou zapečetěné. Typ nelze odvodit z žádného typu hodnoty, například System.Int32. Nelze definovat strukturu, která má dědit z jakékoli uživatelem definované třídy nebo struktury, protože struktura může dědit pouze z System.ValueType. Struktura však může implementovat jedno nebo více rozhraní. Typ struktury můžete přetypovat na libovolný typ rozhraní, který implementuje. Operace zabalení způsobí, že struktura bude uvnitř referenčního typu objektu na spravované haldě. Operace boxování probíhají, když předáte hodnotový typ metodě, která přijímá System.Object nebo jakýkoli typ rozhraní jako vstupní parametr. Další informace naleznete v tématu Boxing and Unboxing.

Pomocí klíčového slova struktury vytvořte vlastní typy hodnot. Struktura se obvykle používá jako kontejner pro malou sadu souvisejících proměnných, jak je znázorněno v následujícím příkladu:

public struct Coords(int x, int y)
{
    public int X { get; init; } = x;
    public int Y { get; init; } = y;
}

Další informace o strukturách naleznete v tématu Typy struktur. Další informace o typech hodnot naleznete v tématu Typy hodnot.

Další kategorie hodnotových typů je enum. Výčet definuje sadu pojmenovaných integrálních konstant. Například System.IO.FileMode výčet v knihovně tříd .NET obsahuje sadu pojmenovaných konstantních celých čísel, které určují, jak má být soubor otevřen. Definuje se, jak je znázorněno v následujícím příkladu:

public enum FileMode
{
    CreateNew = 1,
    Create = 2,
    Open = 3,
    OpenOrCreate = 4,
    Truncate = 5,
    Append = 6,
}

Konstanta System.IO.FileMode.Create má hodnotu 2. Název je ale mnohem smysluplnější pro lidi, kteří čtou zdrojový kód, a proto je lepší místo konstantních literálových čísel použít výčty. Další informace najdete v tématu System.IO.FileMode.

Všechny výčty dědí z System.Enum, který dědí z System.ValueType. Všechna pravidla, která platí pro struktury, platí také pro výčty. Další informace o výčtech naleznete v tématu Výčtové typy.

Typy odkazů

Typ, který definujete jako class, record class, record, delegate, pole nebo interface, je reference type.

Když deklarujete proměnnou reference type, obsahuje hodnotu null , dokud ji nepřiřadíte s instancí tohoto typu nebo ji vytvoříte pomocí operátoru new . Následující příklad ukazuje vytvoření a přiřazení třídy:

MyClass myClass = new();
MyClass myClass2 = myClass;

interface nelze instanciovat přímo pomocí operátoru new. Místo toho vytvořte a přiřaďte instanci třídy, která implementuje rozhraní. Podívejte se na následující příklad:

MyClass myClass = new();

// Declare and assign using an existing value.
IMyInterface myInterface = myClass;

// Or create and assign a value in a single statement.
IMyInterface myInterface2 = new MyClass();

Při vytváření objektu systém přidělí paměť na spravované haldě. Proměnná obsahuje pouze odkaz na umístění objektu. Typy v rámci spravované haldy vyžadují režii jak při jejich přidělení, tak při jejich uvolnění. Uvolňování paměti je funkce automatické správy paměti MODULU CLR, která provádí relamaci. Uvolňování paměti je ale také vysoce optimalizované a ve většině scénářů nevytváří problém s výkonem. Další informace o uvolňování paměti viz Automatická správa paměti.

Všechna pole jsou odkazové typy, i když jsou jejich prvky typy hodnot. Pole se implicitně odvozují z třídy System.Array. Deklarujete a používáte je pomocí zjednodušené syntaxe, kterou jazyk C# poskytuje, jak je znázorněno v následujícím příkladu:

// Declare and initialize an array of integers.
int[] nums = [1, 2, 3, 4, 5];

// Access an instance property of System.Array.
int len = nums.Length;

Odkazové typy plně podporují dědičnost. Při vytváření třídy můžete dědit z jakéhokoli jiného rozhraní nebo třídy, která není definována jako zapečetěná. Ostatní třídy můžou dědit z vaší třídy a přepsat virtuální metody. Další informace o tom, jak vytvořit vlastní třídy, naleznete v tématu Třídy, struktury a záznamy. Další informace o dědičnosti a virtuálních metodách naleznete v tématu Dědičnost.

Typy hodnot literálů

V jazyce C# kompilátor přiřadí typ literálním hodnotám. Způsob zadávání číselného literálu můžete určit tak, že na konec čísla připojíte písmeno. Chcete-li například určit, že hodnota 4.56 by měla být považována floatza , připojte za číslo "f" nebo "F": 4.56f. Pokud nepřipojujete písmeno, kompilátor odvodí typ literálu. Další informace o typech, které můžete zadat pomocí přípon písmen, naleznete v tématu Integrální číselné typy a Číselné typy s plovoucí desetinnou čárkou.

Vzhledem k tomu, že literály jsou zadány a všechny typy jsou odvozeny z System.Object, můžete psát a kompilovat kód, například následující kód:

string s = "The answer is " + 5.ToString();
// Outputs: "The answer is 5"
Console.WriteLine(s);

Type type = 12345.GetType();
// Outputs: "System.Int32"
Console.WriteLine(type);

Obecné typy

Deklarujte typ s jedním nebo více parametry typu , které fungují jako zástupné symboly pro skutečný typ ( konkrétní typ). Klientský kód poskytuje konkrétní typ při vytváření instance typu. Tyto typy se nazývají obecné typy. Například typ System.Collections.Generic.List<T> .NET má jeden parametr typu, který má podle konvence název T. Při vytváření instance typu zadáte typ objektů, které seznam obsahuje, například string:

List<string> stringList = new List<string>();
stringList.Add("String example");
// compile time error adding a type other than a string:
stringList.Add(4);

Použití parametru typu umožňuje opakovaně použít stejnou třídu pro uložení jakéhokoli typu elementu, aniž by bylo nutné převést každý prvek na objekt. Obecné třídy kolekce jsou silně typované kolekce, protože kompilátor zná konkrétní typ elementů kolekce a může vykázat chybu při kompilaci, pokud se například pokusíte přidat celé číslo do objektu stringList v předchozím příkladu. Další informace najdete v tématu Obecné typy.

Řazené kolekce členů a anonymní typy

Vytvoření typu pro jednoduché sady souvisejících hodnot může být nevhodné, pokud tyto hodnoty nechcete ukládat nebo předávat pomocí veřejných rozhraní API. Pro tento účel můžete vytvořit tutiny nebo anonymní typy. Další informace naleznete v tématu n-tice a anonymní typy.

Typy hodnot s povolenou hodnotou Null

Běžné typy hodnot nemohou mít hodnotu null. Typy hodnot s možnou hodnotou null však můžete vytvořit tak, že za ? tento typ připojíte. Jedná se například o typ, int? který může mít také hodnotu int.null Typy hodnot s možnou hodnotou null jsou instance obecného typu System.Nullable<T>struktury . Typy hodnot, které mohou mít hodnotu null, jsou užitečné zejména při předávání dat do az databází, kde číselné hodnoty mohou být null. Další informace naleznete v tématu nulovatelné typy hodnot.

Implicitní deklarace typů

Implicitně zadejte místní proměnnou (ale ne členy třídy) pomocí klíčového var slova. Proměnná stále přijímá typ v době kompilace, ale kompilátor tento typ poskytuje. Další informace naleznete v tématu Implicitně typované místní proměnné.

Typ kompilace a typ běhu

Proměnná může mít různé typy kompilace a doby běhu. Typ kompilace je deklarovaný nebo odvozený typ proměnné ve zdrojovém kódu. Typ běhu je typ instance odkazované danou proměnnou. Tyto dva typy jsou často stejné, jako v následujícím příkladu:

string message = "This is a string of characters";

V jiných případech se typ doby kompilace liší, jak je znázorněno v následujících dvou příkladech:

object anotherMessage = "This is another string of characters";
IEnumerable<char> someCharacters = "abcdefghijklmnopqrstuvwxyz";

V obou předchozích příkladech je typ běhu typu string. Typ času kompilace je object na prvním řádku a IEnumerable<char> ve druhém.

Pokud se oba typy pro proměnnou liší, je důležité pochopit, kdy se používá typ kompilace a typ běhu. Typ času kompilace určuje všechny akce, které kompilátor provede. Tyto akce kompilátoru zahrnují rozlišení volání metod, řešení přetížení a implicitní i explicitní konverze. Typ doby běhu určuje všechny akce, které jsou vyřešeny za běhu. Tyto akce za běhu zahrnují odesílání volání virtuálních metod, vyhodnocování is a switch výrazů a další rozhraní API pro testování typů. Abyste lépe pochopili, jak váš kód komunikuje s typy, rozpoznáte, která akce se vztahuje na který typ.

Další informace najdete v následujících článcích:

Specifikace jazyka C#

Další informace najdete ve specifikaci jazyka C#. Specifikace jazyka je konečným zdrojem syntaxe a použití jazyka C#.