Ingebouwde referentietypen (C#-verwijzing)

C# heeft veel ingebouwde referentietypen. Ze hebben trefwoorden of operatoren die synoniemen zijn voor een type in de .NET-bibliotheek.

Het objecttype

Het object type is een alias voor System.Object in .NET. In het geïntegreerde typesysteem van C# nemen alle typen, vooraf gedefinieerde en door de gebruiker gedefinieerde, referentietypen en waardetypen direct of indirect over van System.Object. U kunt waarden van elk type toewijzen aan variabelen van het type object. Elke object variabele kan worden toegewezen aan de standaardwaarde met behulp van de letterlijke waarde null. Wanneer een variabele van een waardetype wordt geconverteerd naar object, wordt gezegd dat deze in een vak wordt geplaatst. Wanneer een variabele van het type object wordt geconverteerd naar een waardetype, wordt gezegd dat deze is uitgeschakeld. Zie Boxing and Unboxing (Boksen en Uitpakken) voor meer informatie.

Het tekenreekstype

Het string type vertegenwoordigt een reeks van nul of meer Unicode-tekens. string is een alias voor System.String in .NET.

Hoewel string dit een verwijzingstype is, worden de gelijkheidsoperatoren == gedefinieerd en != gedefinieerd om de waarden van string objecten te vergelijken, niet verwijzingen. Gelijkheid op basis van waarde maakt testen op tekenreeks gelijkheid intuïtiever. Voorbeeld:

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

In het vorige voorbeeld wordt 'Waar' en 'Onwaar' weergegeven omdat de inhoud van de tekenreeksen equivalent is, maar ab niet naar hetzelfde tekenreeksexemplaren verwijst.

De +-operator voegt tekenreeksen samen:

string a = "good " + "morning";

Met de voorgaande code wordt een tekenreeksobject gemaakt dat 'goedemorgen' bevat.

Tekenreeksen zijn onveranderbaar. De inhoud van een tekenreeksobject kan niet worden gewijzigd nadat het object is gemaakt. Wanneer u deze code schrijft, maakt de compiler bijvoorbeeld daadwerkelijk een nieuw tekenreeksobject voor de nieuwe reeks tekens en wordt dat nieuwe object toegewezen aan b. Het geheugen waarvoor is toegewezen b (toen deze de tekenreeks h bevatte) komt vervolgens in aanmerking voor garbagecollection.

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

De []operator kan worden gebruikt voor leestoegang tot afzonderlijke tekens van een tekenreeks. Geldige indexwaarden beginnen bij 0 en moeten kleiner zijn dan de lengte van de tekenreeks:

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

Op dezelfde manier kan de [] operator ook worden gebruikt voor het herhalen van elk teken in een tekenreeks:

string str = "test";

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

Letterlijke tekenreeksen

Letterlijke tekenreeksen zijn van het type string en kunnen worden geschreven in drie vormen, onbewerkt, geciteerd en letterlijk.

Onbewerkte letterlijke tekenreeksen zijn beschikbaar vanaf C# 11. Letterlijke tekenreeksen van onbewerkte tekenreeksen kunnen willekeurige tekst bevatten zonder escapereeksen. Letterlijke tekenreeksen voor onbewerkte tekenreeksen kunnen witruimte en nieuwe regels, ingesloten aanhalingstekens en andere speciale tekens bevatten. Letterlijke tekenreeksen van onbewerkte tekenreeksen staan tussen minimaal drie dubbele aanhalingstekens ("""):

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

U kunt zelfs een reeks van drie (of meer) dubbele aanhalingstekens opnemen. Als voor uw tekst een ingesloten reeks aanhalingstekens is vereist, begint en beëindigt u de letterlijke tekenreeks met meer aanhalingstekens, indien nodig:

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

Letterlijke tekenreeksen voor onbewerkte tekenreeksen hebben doorgaans de begin- en eindvolgordes voor aanhalingstekens op afzonderlijke regels van de ingesloten tekst. Letterlijke tekenreeksen met meerdere regels ondersteunen tekenreeksen die zelf tekenreeksen tussen aanhaalt:

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

Wanneer de begin- en eindophalingstekens op afzonderlijke regels staan, worden de nieuwe regels na de aanhalingstekens en voorafgaande aan de eindcitaat niet opgenomen in de uiteindelijke inhoud. De reeks aanhalingstekens sluiten bepaalt de meest linkse kolom voor de letterlijke tekenreeks. U kunt een letterlijke onbewerkte tekenreeks laten inspringen zodat deze overeenkomt met de algemene code-indeling:

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

Kolommen rechts van de eindopgavevolgorde blijven behouden. Dit gedrag maakt onbewerkte tekenreeksen mogelijk voor gegevensindelingen zoals JSON, YAML of XML, zoals wordt weergegeven in het volgende voorbeeld:

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

De compiler geeft een fout op als een van de tekstregels links van de afsluitende aanhalingstekenreeks wordt uitgebreid. De aanhalingstekenreeksen voor openen en sluiten kunnen zich op dezelfde regel bevinden, zodat de letterlijke tekenreeks van de tekenreeks niet begint of eindigt met een aanhalingsteken:

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

U kunt letterlijke tekenreeksen combineren met tekenreeksinterpolatie om aanhalingstekens en accolades in de uitvoertekenreeks op te nemen.

Letterlijke tekenreeks tussen aanhalingstekens tussen dubbele aanhalingstekens ("):

"good morning"  // a string literal

Letterlijke tekenreeksen kunnen elke letterlijke tekenreeks bevatten. Escape-reeksen zijn opgenomen. In het volgende voorbeeld wordt een escape-reeks \\ gebruikt voor backslash, \u0066 voor de letter f en \n voor newline.

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

Notitie

De escapecode \udddd (waarbij dddd een getal van vier cijfers is) vertegenwoordigt het Unicode-teken U+dddd. Unicode-escapecodes met acht cijfers worden ook herkend: \Udddddddd.

Letterlijke letterlijke verbatimtekenreeksen beginnen met @ en worden ook tussen dubbele aanhalingstekens geplaatst. Voorbeeld:

@"good morning"  // a string literal

Het voordeel van verbatimtekenreeksen is dat escapereeksen niet worden verwerkt, waardoor u eenvoudig kunt schrijven. De volgende tekst komt bijvoorbeeld overeen met een volledig gekwalificeerde Windows-bestandsnaam:

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

Als u een dubbel aanhalingsteken wilt opnemen in een tekenreeks met @aanhalingstekens, dubbelt u het volgende:

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

Letterlijke tekenreeksen van UTF-8

Tekenreeksen in .NET worden opgeslagen met UTF-16-codering. UTF-8 is de standaard voor webprotocollen en andere belangrijke bibliotheken. Vanaf C# 11 kunt u het u8 achtervoegsel toevoegen aan een letterlijke tekenreeks om UTF-8-codering op te geven. Letterlijke UTF-8-gegevens worden opgeslagen als ReadOnlySpan<byte> objecten. Het natuurlijke type van een letterlijke UTF-8-tekenreeks is ReadOnlySpan<byte>. Als u een letterlijke UTF-8-tekenreeks gebruikt, wordt een duidelijkere declaratie gemaakt dan het equivalent System.ReadOnlySpan<T>te declareren, zoals wordt weergegeven in de volgende code:

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

Als u een letterlijke UTF-8-tekenreeks wilt opslaan als een matrix, moet u de ReadOnlySpan<T>.ToArray() bytes met de letterlijke waarde naar de onveranderbare matrix kopiëren:

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

Letterlijke waarden voor UTF-8-tekenreeksen zijn geen tijdconstanten; het zijn runtimeconstanten. Daarom kunnen ze niet worden gebruikt als de standaardwaarde voor een optionele parameter. Letterlijke tekenreeksen van UTF-8 kunnen niet worden gecombineerd met tekenreeksinterpolatie. U kunt het $ token en het u8 achtervoegsel niet gebruiken voor dezelfde tekenreeksexpressie.

Het type gemachtigde

De declaratie van een type gemachtigde is vergelijkbaar met een methodehandtekening. Het heeft een retourwaarde en een willekeurig aantal parameters van elk type:

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

In .NET System.Action bieden typen System.Func algemene definities voor veel algemene gemachtigden. Waarschijnlijk hoeft u geen nieuwe aangepaste gedelegeerdentypen te definiëren. In plaats daarvan kunt u instantiëringen maken van de opgegeven algemene typen.

A delegate is een verwijzingstype dat kan worden gebruikt om een benoemde of anonieme methode in te kapselen. Gemachtigden zijn vergelijkbaar met functiepointers in C++; Gemachtigden zijn echter type-veilig en veilig. Zie Gemachtigden en Algemene gemachtigden voor toepassingen van gemachtigden. Gemachtigden vormen de basis voor gebeurtenissen. Een gemachtigde kan worden geïnstantieerd door deze te koppelen aan een benoemde of anonieme methode.

De gemachtigde moet worden geïnstantieerd met een methode of lambda-expressie met een compatibel retourtype en invoerparameters. Zie Afwijking in Gemachtigden voor meer informatie over de mate van variantie die is toegestaan in de handtekening van de methode. Voor gebruik met anonieme methoden worden de gemachtigde en de code die eraan moet worden gekoppeld samen gedeclareerd.

De combinatie of verwijdering van gemachtigden mislukt met een runtime-uitzondering wanneer de gedelegeerdentypen die tijdens de uitvoering zijn betrokken, verschillen vanwege variantconversie. In het volgende voorbeeld ziet u een situatie die mislukt:

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;

U kunt een gemachtigde maken met het juiste runtimetype door een nieuw gedelegeerde-object te maken. In het volgende voorbeeld ziet u hoe deze tijdelijke oplossing kan worden toegepast op het voorgaande voorbeeld.

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;

U kunt functie-aanwijzers declareren, die vergelijkbare syntaxis gebruiken. Een functiepointer gebruikt de calli instructie in plaats van een gemachtigde te instantiëren en de virtuele Invoke methode aan te roepen.

Het dynamische type

Het dynamic type geeft aan dat het gebruik van de variabele en verwijzingen naar de leden de controle van het type compileertijd overslaan. In plaats daarvan worden deze bewerkingen tijdens runtime opgelost. Het dynamic type vereenvoudigt de toegang tot COM-API's, zoals de Office Automation-API's, tot dynamische API's zoals IronPython-bibliotheken en het HTML Document Object Model (DOM).

Type dynamic gedraagt zich als het type object in de meeste omstandigheden. Met name elke niet-null-expressie kan worden geconverteerd naar het dynamic type. Het dynamic type verschilt van object in die bewerkingen die expressies van het type dynamic bevatten, worden niet omgezet of het type gecontroleerd door de compiler. De compiler verpakt informatie over de bewerking en die informatie wordt later gebruikt om de bewerking tijdens runtime te evalueren. Als onderdeel van het proces worden variabelen van het type dynamic gecompileerd in variabelen van het type object. Daarom bestaat het type dynamic alleen tijdens het compileren, niet tijdens runtime.

In het volgende voorbeeld wordt een variabele van het type dynamic vergeleken met een variabele van het type object. Als u het type van elke variabele tijdens het compileren wilt controleren, plaatst u de muiswijzer op dyn of obj in de WriteLine instructies. Kopieer de volgende code naar een editor waar IntelliSense beschikbaar is. IntelliSense toont dynamisch voor dyn en object voor 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());
    }
}

In WriteLine de instructies worden de runtimetypen dyn en obj. Op dat moment hebben beide hetzelfde type, geheel getal. De volgende uitvoer wordt geproduceerd:

System.Int32
System.Int32

Als u het verschil tussen dyn en obj tijdens het compileren wilt zien, voegt u de volgende twee regels toe tussen de declaraties en de WriteLine instructies in het vorige voorbeeld.

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

Er wordt een compilerfout gerapporteerd voor het toevoegen van een geheel getal en een object in expressie obj + 3. Er wordt echter geen fout gerapporteerd voor dyn + 3. De expressie die bevat dyn , wordt niet gecontroleerd tijdens het compileren omdat het type dyn is dynamic.

In het volgende voorbeeld worden verschillende declaraties gebruikt dynamic . De Main methode contrasteert ook het controleren van het type compileertijd met het controleren van runtime-typen.

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#-taalspecificatie

Zie de volgende secties van de C#-taalspecificatie voor meer informatie:

Zie ook