Freigeben über


Integrierte Verweistypen (C#-Referenz)

C# umfasst eine Vielzahl von integrierten Verweistypen. Diese Typen enthalten Schlüsselwörter oder Operatoren, die Synonyme für einen Typ in der .NET-Bibliothek sind.

Die C#-Sprachreferenz dokumentiert die zuletzt veröffentlichte Version der C#-Sprache. Außerdem enthält sie erste Dokumentation für Features in der öffentlichen Vorschau für die kommende Sprachversion.

In der Dokumentation werden alle Features identifiziert, die in den letzten drei Versionen der Sprache oder in der aktuellen öffentlichen Vorschau eingeführt wurden.

Tipp

Informationen dazu, wann ein Feature erstmals in C# eingeführt wurde, finden Sie im Artikel zum Versionsverlauf der C#-Sprache.

den Objekttyp

Der object-Typ ist ein Alias für System.Object in .NET. Im vereinheitlichen Typsystem von C# erben alle Typen, vordefiniert und benutzerdefiniert sowie Verweis- und Werttypen, direkt oder indirekt von System.Object. Weisen Sie Variablen des Typs objectWerte eines beliebigen Typs zu (mit Ausnahmeref struct, siehe Verweisstruktur). Sie können das Literal null einer beliebigen object Variablen als Standardwert zuweisen. Wenn Sie eine Werttypvariable objectin konvertieren, wird der Wert boxt. Wenn Sie eine Variable vom Typ object in einen Werttyp konvertieren, wird der Wert unboxed. Weitere Informationen finden Sie unter Boxing und Unboxing.

Der Zeichenfolgentyp

Der Typ string stellt eine Sequenz von Null oder mehr Unicode-Zeichen dar. In .NET string ist ein Alias für System.String.

Obwohl string es sich um einen Verweistyp handelt, werden die Gleichheitsoperatoren == und != die Werte von string Objekten verglichen, nicht auf Verweise. Wertbasierte Gleichheit macht Tests für die Zeichenfolgengleichstellung intuitiver. Beispiel:

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

Im vorherigen Beispiel wird "True" und dann "False" angezeigt, da der Inhalt der Zeichenfolgen gleichwertig ist, aber ab nicht auf dieselbe Zeichenfolgeninstanz verweist.

Der Operator „+“ verkettet Zeichenfolgen:

string a = "good " + "morning";

Der obige Code erstellt ein Zeichenfolgenobjekt, das „good morning“ enthält.

Zeichenfolgen sind unveränderlich . Sie können den Inhalt eines Zeichenfolgenobjekts nach dem Erstellen nicht mehr ändern. Wenn Sie beispielsweise diesen Code schreiben, erstellt der Compiler tatsächlich ein neues Zeichenfolgenobjekt, um die neue Abfolge von Zeichen zu enthalten, und weist dieses neue Objekt zu b. Der zugewiesene b Speicher (wenn er die Zeichenfolge "h") enthält, ist dann für die Garbage Collection berechtigt.

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

Sie können den []Operator für den schreibgeschützten Zugriff auf einzelne Zeichen einer Zeichenfolge verwenden. Gültige Indexwerte beginnen bei 0 und müssen kleiner als die Länge der Zeichenfolge sein:

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

In ähnlicher Weise können Sie den [] Operator zum Durchlaufen der einzelnen Zeichen in einer Zeichenfolge verwenden:

string str = "test";

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

Zeichenfolgenliterale

Zeichenfolgenliterale sind typisiert string und in drei Formen enthalten: roh, zitiert und vertbatim.

Unformatierte Zeichenfolgenliterale enthalten beliebigen Text ohne Escapesequenzen. Unformatierte Zeichenfolgenliterale können Leerzeichen und Zeilenumbrüche, eingebettete Anführungszeichen und andere Sonderzeichen enthalten. Unformatierte Zeichenfolgenliterale sind in mindestens drei doppelte Anführungszeichen (""") eingeschlossen:

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

Sie können sogar eine Sequenz von drei (oder mehr) doppelten Anführungszeichen verwenden. Wenn der Text eine eingebettete Sequenz von Anführungszeichen erfordert, müssen Sie das unformatierte Zeichenfolgenliteral je nach Bedarf mit weiteren Anführungszeichen beginnen und beenden:

"""""
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.
"""""

Bei unformatierten Zeichenfolgenliteralen befinden sich die Sequenzen von Anführungszeichen am Anfang und Ende in der Regel in getrennten Zeilen vom eingebetteten Text. Mehrzeilige unformatierte Zeichenfolgenliterale unterstützen Zeichenfolgen, die selbst Zeichenfolgen in Anführungszeichen sind:

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

Wenn sich die Anführungszeichen am Anfang und Ende in separaten Zeilen befinden, sind die Zeilenumbrüche nach den öffnenden Anführungszeichen und vor dem schließenden Anführungszeichen nicht im endgültigen Inhalt enthalten. Die schließende Anführungszeichensequenz legt die linke Spalte für das Zeichenfolgenliteral fest. Sie können ein unformatiertes Zeichenfolgenliteral einrücken, damit es dem verwendeten Codeformat entspricht:

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

Spalten rechts neben der schließenden Anführungszeichensequenz werden beibehalten. Dieses Verhalten ermöglicht unformatierte Zeichenfolgen für Datenformate wie JSON, YAML oder XML, wie im folgenden Beispiel gezeigt:

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

Tipp

Visual Studio und das C# Dev Kit bieten einige Überprüfungs- und Syntaxmarkierungen, wenn unformatierte Zeichenfolgenliterale JSON-Daten oder reguläre Ausdrücke enthalten.

Die Tools analysieren den Text. Wenn die Tools vertrauen, dass der Text JSON oder einen regulären Ausdruck darstellt, stellt der Editor Syntaxfarben bereit.

Sie können diese Erfahrung verbessern, indem Sie einen Kommentar oberhalb der Deklaration hinzufügen, der das Format angibt:

  • // lang=json gibt an, dass das unformatierte Zeichenfolgenliteral JSON-Daten darstellt.
  • // lang=regex gibt an, dass das unformatierte Zeichenfolgenliteral einen regulären Ausdruck darstellt.

Wenn ein unformatiertes Zeichenfolgenliteral als Argument verwendet wird, bei dem der Parameter das System.Diagnostics.CodeAnalysis.StringSyntaxAttribute Format angibt, überprüfen diese Tools das unformatierte Zeichenfolgenliteral für einige der Formattypen. Sowohl JSON als auch regex werden unterstützt.

Bei einigen Formaten ermöglicht der Kommentar oder das Attribut Codevorschläge Korrekturen für Zeichenfolgenliterale basierend auf dem Format.

Der Compiler gibt einen Fehler zurück, wenn eine der Textzeilen links neben der schließenden Anführungszeichensequenz erweitert wird. Die öffnenden und schließenden Anführungszeichensequenz können sich in derselben Zeile befinden, sofern der Zeichenfolgenliteral weder mit einem Anführungszeichen beginnt noch damit endet:

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

Sie können unformatierte Zeichenfolgenliterale mit der Zeichenfolgeninterpolation kombinieren, um Anführungszeichen und Klammern in die Ausgabezeichenfolge einzuschließen.

Zeichenfolgenliterale in Anführungszeichen werden in doppelte Anführungszeichen (") eingeschlossen:

"good morning"  // a string literal

Zeichenfolgenliterale können jeden Zeichenliteral enthalten. Escapesequenzen sind enthalten. Im folgenden Beispiel wird die Escapesequenz \\ für den umgekehrten Schrägstrich, \u0066 für den Buchstaben „f“ und \n für den Zeilenumbruch verwendet.

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

Hinweis

Der Escapecode \udddd (wobei dddd eine vierstellige Zahl ist) stellt das Unicode-Zeichen U+dddd dar. Escapecodes aus achtstelligen Unicode werden auch erkannt: \Udddddddd.

Wörtliche Zeichenfolgenliterale beginnen mit @ und sind ebenfalls in doppelte Anführungszeichen eingeschlossen. Beispiel:

@"good morning"  // a string literal

Der Vorteil von Verbatim-Zeichenfolgen besteht darin, dass Escapesequenzen nicht verarbeitet werden , wodurch sie einfach geschrieben werden können. Der folgende Text entspricht beispielsweise einem vollqualifizierten Windows-Dateinamen:

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

Um ein doppeltes Anführungszeichen in eine @-Zeichenfolge aufzunehmen, müssen Sie es verdoppeln:

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

UTF-8-Zeichenfolgenliterale

.NET speichert Zeichenfolgen mit UTF-16-Codierung. UTF-8 ist der Standard für Webprotokolle und andere wichtige Bibliotheken. Sie können das u8 Suffix einem Zeichenfolgenliteral hinzufügen, um UTF-8-Codierung anzugeben. Der Compiler speichert UTF-8-Literale als ReadOnlySpan<byte> Objekte. Durch Verwendung eines UTF-8-Zeichenfolgenliterals entsteht eine klarere Deklaration als beim Deklarieren des entsprechenden System.ReadOnlySpan<T>, wie im folgenden Code gezeigt:

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

Um ein UTF-8-Zeichenfolgenliteral als Array zu speichern, kopieren Sie ReadOnlySpan<T>.ToArray() die Bytes, die das Literal enthalten, in das veränderbare Array:

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

UTF-8-Zeichenfolgenliterale sind keine Kompilierungszeitkonstanten; sie sind Laufzeitkonstanten. Daher können sie nicht als Standardwert für einen optionalen Parameter verwendet werden. Sie können UTF-8-Zeichenfolgenliterale nicht mit einer Zeichenfolgeninterpolation kombinieren. Sie können das $-Token und das u8-Suffix nicht für denselben Zeichenfolgenausdruck verwenden.

Der Delegattyp

Die Deklaration eines Delegattyps ähnelt einer Methodensignatur. Er verfügt über einen Rückgabewert und eine beliebige Anzahl Parameter eines beliebigen Typs:

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

In .NET stellen die Typen System.Action und System.Func generische Definitionen für zahlreiche allgemeine Delegaten bereit. Sie werden sehr wahrscheinlich keine neuen benutzerdefinierten Delegattypen definieren müssen. Stattdessen können Sie Instanziierungen der bereitgestellten generischen Typen erstellen.

A delegate ist ein integrierter Verweistyp, mit dem Sie eine benannte oder anonyme Methode kapseln können. Delegaten entsprechen den Funktionszeigern in C++, sind jedoch typsicher und geschützt. Anwendungsmöglichkeiten von Delegaten finden Sie unter Delegaten und Generische Delegaten. Delegaten bilden die Grundlage für Ereignisse. Sie instanziieren eine Stellvertretung, indem Sie sie entweder einer benannten oder anonymen Methode zuordnen.

Sie müssen den Delegaten mit einer Methode oder einem Lambda-Ausdruck instanziieren, der über einen kompatiblen Rückgabetyp und Eingabeparameter verfügt. Weitere Informationen zum Grad der Varianz, der in der Methodensignatur zulässig ist, finden Sie unter Varianz bei Delegaten. Deklarieren Sie für die Verwendung mit anonymen Methoden den Delegat und den Code, der zusammen damit verknüpft werden soll.

Das Kombinieren oder Entfernen von Delegaten schlägt mit einer Laufzeitausnahme fehl, wenn die zur Laufzeit beteiligten Delegattypen aufgrund der Variantenkonvertierung unterschiedlich sind. Im folgenden Beispiel wird eine Situation veranschaulicht, in der ein Fehler auftritt:

Action<string> stringAction = str => {};
Action<object> objectAction = obj => {};

// Valid due to implicit reference conversion of
// objectAction to Action<string>, but might fail
// at run time.
Action<string> combination = stringAction + objectAction;

Sie können einen Delegaten mit dem richtigen Laufzeittyp erstellen, indem Sie ein neues Delegatobjekt erstellen. Im folgenden Beispiel wird veranschaulicht, wie diese Problemumgehung auf das vorherige Beispiel angewendet werden kann.

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;

Sie können Funktionszeiger deklarieren, die eine ähnliche Syntax verwenden. Ein Funktionszeiger verwendet die calli-Anweisung, anstatt einen Delegattyp zu instanziieren und die virtuelle Invoke-Methode aufzurufen.

Der dynamische Typ

Der dynamic Typ gibt an, dass die Variable und verweise auf ihre Member die Kompilierungszeittypüberprüfung umgehen. Stattdessen werden diese Vorgänge zur Laufzeit aufgelöst. Der dynamic-Typ vereinfacht den Zugriff auf COM-APIs, z. B. die Office Automation-APIs, auf dynamische APIs, beispielsweise IronPython-Bibliotheken und auf das HTML-Dokumentobjektmodell (Document Object Model, DOM).

Der Typ dynamic verhält sich in den meisten Fällen wie Typ object. Insbesondere können alle Ausdrücke, die nicht NULL sind, in den dynamic-Typ konvertiert werden. Der dynamic Typ unterscheidet sich von object dem, dass der Compiler vorgänge, die Ausdrücke des Typs dynamicenthalten, nicht aufgelöst oder Typüberprüfungsvorgänge enthält. Der Compiler packt Informationen über den Vorgang. Diese Informationen werden später zur Evaluierung des Vorgangs zur Laufzeit verwendet. Als Teil dieses Prozesses werden Variablen des Typs dynamic in Variablen des Typs object kompiliert. Deshalb existiert der Typ dynamic nur zur Kompilierzeit und nicht zur Laufzeit.

Im folgenden Beispiel wird eine Variable vom Typ dynamic mit einer Variablen vom Typ objectkontrastiert. Um den Typ jeder Variable zur Kompilierzeit zu überprüfen, zeigen Sie mit dem Mauszeiger auf dyn oder obj in den WriteLine-Anweisungen. Kopieren Sie den folgenden Code in einen Editor, in dem IntelliSense verfügbar ist. IntelliSense zeigt dynamic für dyn und object für 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());
    }
}

Die WriteLine-Anweisungen zeigen die Laufzeittypen von dyn und obj. Zu diesem Zeitpunkt verfügen beide denselben Typ, Integer. Es wird die folgende Ausgabe generiert:

System.Int32
System.Int32

Um den Unterschied zwischen dyn und obj zur Kompilierzeit anzuzeigen, fügen Sie die folgenden zwei Zeilen zwischen die Deklarationen und die WriteLine-Anweisungen im vorherigen Beispiel ein.

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

Es wird ein Kompilierfehler für den Versuch, einen Integer und ein Objekt im Ausdruck obj + 3 einzufügen, ausgegeben. Es wird jedoch kein Fehler für dyn + 3 gemeldet. Der Ausdruck, der dyn enthält, wird nicht zur Kompilierzeit überprüft, da der Typ von dyndynamic ist.

Das folgende Beispiel verwendet dynamic in einigen Deklarationen. Die Main-Methode unterscheidet auch die Typüberprüfung zur Kompilierzeit und die Laufzeittypüberprüfung.

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

C#-Sprachspezifikation

Weitere Informationen finden Sie in den folgenden Abschnitten der C#-Sprachspezifikation:

Weitere Informationen