Předdefinované typy odkazů (referenční dokumentace jazyka C#)

Jazyk C# má mnoho předdefinovaných referenčních typů. Mají klíčová slova nebo operátory, které jsou synonymy pro typ v knihovně .NET.

Typ objektu

Typ object je alias pro System.Object v .NET. V systému sjednocených typů jazyka C# všechny typy, předdefinované a uživatelem definované typy odkazů a typy hodnot, dědí přímo nebo nepřímo z System.Object. Hodnoty libovolného typu můžete přiřadit proměnným typu object. Libovolnou object proměnnou lze přiřadit k výchozí hodnotě pomocí literálu null. Když je proměnná typu hodnoty převedena na objekt, říká se, že je pole. Když je proměnná typu object převedena na typ hodnoty, říká se, že je unboxed. Další informace naleznete v tématu Boxing a Unboxing.

Typ řetězce

Typ string představuje posloupnost nula nebo více znaků Unicode. string je alias pro System.String v .NET.

I když string je typ odkazu, operátory == rovnosti a != jsou definovány pro porovnání hodnot string objektů, nikoli odkazů. Rovnost na základě hodnot zvyšuje intuitivnější testování rovnosti řetězců. Příklad:

string a = "hello";
string b = "h";
// Append to contents of 'b'
b += "ello";
Console.WriteLine(a == b);
Console.WriteLine(object.ReferenceEquals(a, b));

Předchozí příklad zobrazí hodnotu True a pak False, protože obsah řetězců je ekvivalentní, ale ab neodkazuje na stejnou instanci řetězce.

Operátor + zřetězí řetězce:

string a = "good " + "morning";

Předchozí kód vytvoří řetězcový objekt, který obsahuje "dobré ráno".

Řetězce jsou neměnné – obsah objektu řetězce nelze po vytvoření objektu změnit. Když například napíšete tento kód, kompilátor ve skutečnosti vytvoří nový řetězcový objekt, který bude obsahovat novou posloupnost znaků a tento nový objekt je přiřazen .b Paměť, pro kterou byla přidělena b (když obsahoval řetězec "h") je pak způsobilá pro uvolňování paměti.

string b = "h";
b += "ello";

Operátor []lze použít pro přístup jen pro čtení k jednotlivým znakům řetězce. Platné hodnoty indexu začínají 0 a musí být menší než délka řetězce:

string str = "test";
char x = str[2];  // x = 's';

Podobně [] lze operátor použít také k iterování jednotlivých znaků v řetězci:

string str = "test";

for (int i = 0; i < str.Length; i++)
{
  Console.Write(str[i] + " ");
}
// Output: t e s t

Řetězcové literály

Řetězcové literály jsou typu string a dají se psát ve třech formách, nezpracovaných, uvozených a doslovných.

Nezpracované řetězcové literály jsou dostupné od C# 11. Nezpracované řetězcové literály mohou obsahovat libovolný text bez nutnosti řídicích sekvencí. Nezpracované řetězcové literály můžou obsahovat prázdné znaky a nové řádky, vložené uvozovky a další speciální znaky. Nezpracované řetězcové literály jsou uzavřeny minimálně do tří dvojitých uvozovek ("""):

"""
This is a multi-line
    string literal with the second line indented.
"""

Můžete dokonce zahrnout posloupnost tří (nebo více) dvojitých uvozovek. Pokud text vyžaduje vloženou posloupnost uvozovek, začnete a ukončete nezpracovaný řetězcový literál s dalšími uvozovkami podle potřeby:

"""""
This raw string literal has four """", count them: """" four!
embedded quote characters in a sequence. That's why it starts and ends
with five double quotes.

You could extend this example with as many embedded quotes as needed for your text.
"""""

Nezpracované řetězcové literály obvykle obsahují počáteční a koncovou posloupnost uvozovek na samostatných řádcích od vloženého textu. Víceřádkové řetězcové literály podporují řetězce, které jsou samy uvozovány řetězce:

var message = """
"This is a very important message."
""";
Console.WriteLine(message);
// output: "This is a very important message."

Pokud jsou počáteční a koncové uvozovky na samostatných řádcích, nové řádky za levou uvozovkou a před koncovou uvozovkou se do konečného obsahu nezahrnou. Uzavírací sekvence uvozovek určuje sloupec úplně vlevo pro řetězcový literál. Odsazení nezpracovaného řetězcového literálu tak, aby odpovídalo celkovému formátu kódu:

var message = """
    "This is a very important message."
    """;
Console.WriteLine(message);
// output: "This is a very important message."
// The leftmost whitespace is not part of the raw string literal

Sloupce napravo od koncové sekvence uvozovek se zachovají. Toto chování umožňuje nezpracované řetězce pro datové formáty, jako jsou JSON, YAML nebo XML, jak je znázorněno v následujícím příkladu:

var json= """
    {
        "prop": 0
    }
    """;

Kompilátor vydá chybu, pokud se některý z textových řádků rozšíří nalevo od uzavírací sekvence uvozovek. Levá a koncová sekvence uvozovek mohou být na stejném řádku a poskytují řetězcový literál, který nezačíná ani končí znakem uvozovek:

var shortText = """He said "hello!" this morning.""";

Nezpracované řetězcové literály můžete kombinovat s interpolací řetězců a zahrnout do výstupního řetězce znaky a závorky.

Řetězcové literály v uvozovkách jsou uzavřeny do dvojitých uvozovek ("):

"good morning"  // a string literal

Řetězcové literály mohou obsahovat libovolný literál znaků. Řídicí sekvence jsou zahrnuté. Následující příklad používá řídicí sekvenci pro zpětné lomítko \\ , \u0066 písmeno f a \n pro nový řádek.

string a = "\\\u0066\n F";
Console.WriteLine(a);
// Output:
// \f
//  F

Poznámka

Řídicí kód \udddd (kde dddd je čtyřmístné číslo) představuje znak Unicode U+dddd. Jsou rozpoznány také osmiciferné řídicí kódy Unicode: \Udddddddd.

Doslovné řetězcové literály začínají @ a jsou také uzavřeny do dvojitých uvozovek. Příklad:

@"good morning"  // a string literal

Výhodou doslovných řetězců je, že řídicí sekvence se nezpracují , což usnadňuje zápis. Například následující text odpovídá plně kvalifikovanému názvu souboru windows:

@"c:\Docs\Source\a.txt"  // rather than "c:\\Docs\\Source\\a.txt"

Pokud chcete do řetězce s uvozovkou @-quoted zahrnout dvojitou uvozovku, poklikejte na ni:

@"""Ahoy!"" cried the captain." // "Ahoy!" cried the captain.

Řetězcové literály UTF-8

Řetězce v .NET se ukládají pomocí kódování UTF-16. UTF-8 je standard pro webové protokoly a další důležité knihovny. Od C# 11 můžete přidat příponu u8 do řetězcového literálu pro určení kódování UTF-8. Literály UTF-8 se ukládají jako ReadOnlySpan<byte> objekty. Přirozený typ řetězcového literálu UTF-8 je ReadOnlySpan<byte>. Použití řetězcového literálu UTF-8 vytvoří jasnější deklaraci než deklarování ekvivalentu System.ReadOnlySpan<T>, jak je znázorněno v následujícím kódu:

ReadOnlySpan<byte> AuthWithTrailingSpace = new byte[] { 0x41, 0x55, 0x54, 0x48, 0x20 };
ReadOnlySpan<byte> AuthStringLiteral = "AUTH "u8;

Pokud chcete uložit řetězcový literál UTF-8 jako pole, je nutné použít ReadOnlySpan<T>.ToArray() ke zkopírování bajtů obsahujících literál do proměnlivého pole:

byte[] AuthStringLiteral = "AUTH "u8.ToArray();

Řetězcové literály UTF-8 nejsou časové konstanty kompilace; jsou konstanty modulu runtime. Proto je nelze použít jako výchozí hodnotu volitelného parametru. Řetězcové literály UTF-8 nelze kombinovat s interpolací řetězců. Token a příponu $u8 nemůžete použít ve stejném řetězcovém výrazu.

Typ delegáta

Deklarace typu delegáta je podobná podpisu metody. Má návratovou hodnotu a libovolný počet parametrů libovolného typu:

public delegate void MessageDelegate(string message);
public delegate int AnotherDelegate(MyType m, long num);

V .NET System.Action a System.Func typy poskytují obecné definice pro mnoho běžných delegátů. Pravděpodobně nemusíte definovat nové vlastní typy delegátů. Místo toho můžete vytvořit instance zadaných obecných typů.

A delegate je typ odkazu, který lze použít k zapouzdření pojmenované nebo anonymní metody. Delegáti se podobají ukazatelům funkcí v jazyce C++; Delegáti jsou však typově bezpečná a zabezpečená. Aplikace delegátů najdete v tématu Delegáti a obecné delegáty. Delegáti jsou základem událostí. Delegáta lze vytvořit tak, že ho přidružuje pojmenovanou nebo anonymní metodou.

Delegát musí být vytvořený pomocí metody nebo výrazu lambda, který má kompatibilní návratový typ a vstupní parametry. Další informace o stupni odchylky, která je povolena v podpisu metody, naleznete v tématu Variance in Delegates. Pro použití s anonymními metodami se delegát a kód, které se mají k němu přidružit, jsou deklarovány společně.

Kombinace nebo odebrání delegáta selže s výjimkou modulu runtime, pokud se typy delegátů v době běhu liší kvůli převodu variant. Následující příklad ukazuje situaci, která selže:

Action<string> stringAction = str => {};
Action<object> objectAction = obj => {};
  
// Valid due to implicit reference conversion of
// objectAction to Action<string>, but may fail
// at run time.
Action<string> combination = stringAction + objectAction;

Delegáta se správným typem modulu runtime můžete vytvořit vytvořením nového objektu delegáta. Následující příklad ukazuje, jak se toto alternativní řešení může použít u předchozího příkladu.

Action<string> stringAction = str => {};
Action<object> objectAction = obj => {};
  
// Creates a new delegate instance with a runtime type of Action<string>.
Action<string> wrappedObjectAction = new Action<string>(objectAction);

// The two Action<string> delegate instances can now be combined.
Action<string> combination = stringAction + wrappedObjectAction;

Počínaje jazykem C# 9 můžete deklarovat ukazatele na funkce, které používají podobnou syntaxi. Ukazatel funkce místo vytvoření instance typu delegáta a volání virtuální Invoke metody používá calli instrukce.

Dynamický typ

Typ dynamic označuje, že použití proměnné a odkazy na její členy obejít kontrolu typu kompilace v čase. Místo toho se tyto operace vyřeší za běhu. Tento dynamic typ zjednodušuje přístup k rozhraním COM API, jako jsou rozhraní API služby Office Automation, k dynamickým rozhraním API, jako jsou knihovny IronPython, a k modelu DOM (Document Object Model).

Typ dynamic se chová jako typ object za většiny okolností. Konkrétně lze libovolný výraz bez hodnoty null převést na dynamic typ. Typ dynamic se liší od object operací, které obsahují výrazy typu dynamic , nejsou přeloženy nebo typ kontrolovány kompilátorem. Kompilátor vytvoří dohromady informace o operaci a informace o této operaci se později použijí k vyhodnocení operace za běhu. V rámci procesu se proměnné typu dynamic kompilují do proměnných typu object. Proto typ dynamic existuje pouze v době kompilace, ne za běhu.

Následující příklad kontrastuje proměnnou typu dynamic na proměnnou typu .object Pokud chcete ověřit typ každé proměnné v době kompilace, umístěte ukazatel myši na dyn příkazy nebo obj do WriteLine nich. Zkopírujte následující kód do editoru, ve kterém je k dispozici IntelliSense. IntelliSense zobrazuje dynamické objekty a dyndynamické objekty pro obj.

class Program
{
    static void Main(string[] args)
    {
        dynamic dyn = 1;
        object obj = 1;

        // Rest the mouse pointer over dyn and obj to see their
        // types at compile time.
        System.Console.WriteLine(dyn.GetType());
        System.Console.WriteLine(obj.GetType());
    }
}

Příkazy WriteLine zobrazují typy dyn spuštění a obj. V tomto okamžiku mají oba typy stejné celé číslo. Vytvoří se následující výstup:

System.Int32
System.Int32

Pokud chcete zjistit rozdíl mezi dyn a obj v době kompilace, přidejte mezi deklarace a WriteLine příkazy v předchozím příkladu následující dva řádky.

dyn = dyn + 3;
obj = obj + 3;

Při pokusu o přidání celého čísla a objektu ve výrazu obj + 3je hlášena chyba kompilátoru . Pro dyn + 3. Výraz, který obsahuje dyn , není kontrolován v době kompilace, protože typ dyn je dynamic.

Následující příklad se používá dynamic v několika deklaracích. Metoda Main také kontrastuje kontrolu typů kompilace s kontrolou typů za běhu.

using System;

namespace DynamicExamples
{
    class Program
    {
        static void Main(string[] args)
        {
            ExampleClass ec = new ExampleClass();
            Console.WriteLine(ec.exampleMethod(10));
            Console.WriteLine(ec.exampleMethod("value"));

            // The following line causes a compiler error because exampleMethod
            // takes only one argument.
            //Console.WriteLine(ec.exampleMethod(10, 4));

            dynamic dynamic_ec = new ExampleClass();
            Console.WriteLine(dynamic_ec.exampleMethod(10));

            // Because dynamic_ec is dynamic, the following call to exampleMethod
            // with two arguments does not produce an error at compile time.
            // However, it does cause a run-time error.
            //Console.WriteLine(dynamic_ec.exampleMethod(10, 4));
        }
    }

    class ExampleClass
    {
        static dynamic field;
        dynamic prop { get; set; }

        public dynamic exampleMethod(dynamic d)
        {
            dynamic local = "Local variable";
            int two = 2;

            if (d is int)
            {
                return local;
            }
            else
            {
                return two;
            }
        }
    }
}
// Results:
// Local variable
// 2
// Local variable

specifikace jazyka C#

Další informace najdete v následujících částech specifikace jazyka C#:

Viz také