Inbyggda referenstyper (C#-referens)

C# har många inbyggda referenstyper. De har nyckelord eller operatorer som är synonymer för en typ i .NET-biblioteket.

Objekttypen

Typen object är ett alias för System.Object i .NET. I C#-systemets enhetliga typ ärver alla typer, fördefinierade och användardefinierade, referenstyper och värdetyper, direkt eller indirekt från System.Object. Du kan tilldela värden av valfri typ till variabler av typen object. Alla object variabler kan tilldelas till standardvärdet med hjälp av literalen null. När en variabel av en värdetyp konverteras till ett objekt, sägs den vara rutad. När en variabel av typen object konverteras till en värdetyp sägs den vara oboxad. Mer information finns i Boxning och Avboxning.

Strängtypen

Typen string representerar en sekvens med noll eller fler Unicode-tecken. string är ett alias för System.String i .NET.

Även om string det är en referenstyp definieras likhetsoperatorerna == och != för att jämföra objektens string värden, inte referenser. Värdebaserad likhet gör testning för strängjämlikhet mer intuitivt. Till exempel:

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

I föregående exempel visas "True" och sedan "False" eftersom innehållet i strängarna är likvärdigt, men a och b refererar inte till samma stränginstans.

Operatorn + sammanfogar strängar:

string a = "good " + "morning";

Föregående kod skapar ett strängobjekt som innehåller "god morgon".

Strängar är oföränderliga – innehållet i ett strängobjekt kan inte ändras när objektet har skapats. När du till exempel skriver den här koden skapar kompilatorn faktiskt ett nytt strängobjekt som innehåller den nya teckensekvensen och det nya objektet tilldelas till b. Det minne som hade allokerats för b (när det innehöll strängen "h") är sedan berättigat till skräpinsamling.

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

Operatorn [] kan användas för skrivskyddad åtkomst till enskilda tecken i en sträng. Giltiga indexvärden börjar vid 0 och måste vara mindre än strängens längd:

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

På liknande sätt kan operatorn [] också användas för iterering över varje tecken i en sträng:

string str = "test";

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

Strängliteraler

Strängliteraler är av typen string och kan skrivas i tre former, råa, citerade och ordagrant.

Råsträngliteraler är tillgängliga från och med C# 11. Råsträngliteraler kan innehålla godtycklig text utan att kräva escape-sekvenser. Råsträngliteraler kan innehålla blanksteg och nya rader, inbäddade citattecken och andra specialtecken. Råsträngliteraler omges av minst tre dubbla citattecken ("""):

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

Du kan till och med inkludera en sekvens med tre (eller fler) dubbla citattecken. Om texten kräver en inbäddad sekvens med citattecken startar och avslutar du den råa strängliteralen med fler citattecken efter behov:

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

Råsträngliteraler har vanligtvis start- och slutcitatsekvenserna på separata rader från den inbäddade texten. Multiline raw string literals har stöd för strängar som själva är citerade strängar:

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

När start- och slutcitaten finns på separata rader inkluderas inte de nya raderna som följer öppningscitatet och före slutcitaten i det slutliga innehållet. Den avslutande citatsekvensen dikterar kolumnen längst till vänster för strängliteralen. Du kan dra in en rå strängliteral som matchar det övergripande kodformatet:

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

Kolumner till höger om slutcitatsekvensen bevaras. Det här beteendet aktiverar rådatasträngar för dataformat som JSON, YAML eller XML, som du ser i följande exempel:

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

Kompilatorn utfärdar ett fel om någon av textraderna sträcker sig till vänster om den avslutande citatsekvensen. De inledande och avslutande citatsekvenserna kan finnas på samma rad, vilket ger strängliteralen varken startar eller slutar med ett citattecken:

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

Du kan kombinera råa strängliteraler med stränginterpolation för att inkludera citattecken och klammerparenteser i utdatasträngen.

Angivna strängliteraler omges av dubbla citattecken ("):

"good morning"  // a string literal

Strängliteraler kan innehålla valfri teckenliteral. Escape-sekvenser ingår. I följande exempel används escape-sekvens \\ för omvänt snedstreck, \u0066 för bokstaven f och \n för newline.

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

Kommentar

Escape-koden \udddd (där dddd är ett fyrsiffrigt tal) representerar Unicode-tecknet U+dddd. Åttasiffriga Unicode-utrymningskoder identifieras också: \Udddddddd.

Ordagranna strängliteraler börjar med @ och omges också av dubbla citattecken. Till exempel:

@"good morning"  // a string literal

Fördelen med ordagranna strängar är att escape-sekvenser inte bearbetas, vilket gör det enkelt att skriva. Följande text matchar till exempel ett fullständigt kvalificerat Windows-filnamn:

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

Om du vill inkludera ett dubbelt citattecken i en @-citerad sträng, dubblar du det:

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

UTF-8 strängliteraler

Strängar i .NET lagras med UTF-16-kodning. UTF-8 är standard för webbprotokoll och andra viktiga bibliotek. Från och med C# 11 kan du lägga till suffixet u8 i en strängliteral för att ange UTF-8-kodning. UTF-8-literaler lagras som ReadOnlySpan<byte> objekt. Den naturliga typen av en UTF-8-strängliteral är ReadOnlySpan<byte>. Om du använder en UTF-8-strängliteral skapas en tydligare deklaration än att deklarera motsvarande System.ReadOnlySpan<T>, enligt följande kod:

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

Om du vill lagra en UTF-8-strängliteral som en matris måste du använda ReadOnlySpan<T>.ToArray() för att kopiera byte som innehåller literalen till den föränderliga matrisen:

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

UTF-8-strängliteraler kompilerar inte tidskonstanter. de är körningskonstanter. Därför kan de inte användas som standardvärde för en valfri parameter. UTF-8-strängliteraler kan inte kombineras med stränginterpolation. Du kan inte använda $ token och suffixet u8 i samma stränguttryck.

Ombudstypen

Deklarationen av en ombudstyp liknar en metodsignatur. Den har ett returvärde och valfritt antal parametrar av valfri typ:

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

I .NET System.Action tillhandahåller typerna System.Func allmänna definitioner för många vanliga ombud. Du behöver förmodligen inte definiera nya anpassade ombudstyper. I stället kan du skapa instansieringar av de angivna generiska typerna.

A delegate är en referenstyp som kan användas för att kapsla in en namngiven eller anonym metod. Ombud liknar funktionspekare i C++; Ombuden är dock typsäkra och säkra. För program för ombud, se Ombud och Allmänna ombud. Ombud är grunden för händelser. Ett ombud kan instansieras genom att associera det med en namngiven eller anonym metod.

Ombudet måste instansieras med en metod eller ett lambda-uttryck som har en kompatibel returtyp och indataparametrar. Mer information om graden av varians som tillåts i metodsignaturen finns i Varians i Ombud. För användning med anonyma metoder deklareras ombudet och koden som ska associeras med den tillsammans.

Delegatkombinationen eller borttagningen misslyckas med ett körningsundundansfel när de ombudstyper som ingår vid körningen skiljer sig på grund av variantkonvertering. I följande exempel visas en situation som misslyckas:

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;

Du kan skapa ett ombud med rätt körningstyp genom att skapa ett nytt ombudsobjekt. I följande exempel visas hur den här lösningen kan tillämpas på föregående exempel.

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;

Du kan deklarera funktionspekare som använder liknande syntax. En funktionspekare använder instruktionen calli i stället för att instansiera en delegattyp och anropa den virtuella Invoke metoden.

Den dynamiska typen

Typen dynamic anger att användningen av variabeln och referenser till dess medlemmar kringgår kompileringstidstypkontroll. I stället löses dessa åtgärder vid körning. Typen dynamic förenklar åtkomsten till COM-API:er, till exempel Office Automation-API:er, till dynamiska API:er som IronPython-bibliotek och till HTML-dokumentobjektmodellen (DOM).

Typen dynamic beter sig som typ object i de flesta fall. I synnerhet kan alla icke-null-uttryck konverteras till dynamic typen . Typen dynamic skiljer sig från object i att åtgärder som innehåller uttryck av typen dynamic inte matchas eller typ som kontrolleras av kompilatorn. Kompilatorn paketar ihop information om åtgärden och den informationen används senare för att utvärdera åtgärden vid körning. Som en del av processen kompileras variabler av typen dynamic till variabler av typen object. Därför finns typen dynamic endast vid kompileringstillfället, inte vid körning.

I följande exempel kontrasterar en variabel av typen dynamic till en variabel av typen object. Om du vill verifiera typen av varje variabel vid kompileringstillfället placerar du muspekaren över dyn eller obj i -uttrycken WriteLine . Kopiera följande kod till ett redigeringsprogram där IntelliSense är tillgängligt. IntelliSense visar dynamiskt för dyn och objekt 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());
    }
}

Uttrycken WriteLine visar körningstyperna dyn för och obj. Då har båda samma typ, heltal. Följande utdata genereras:

System.Int32
System.Int32

Om du vill se skillnaden mellan dyn och obj vid kompileringstillfället lägger du till följande två rader mellan deklarationerna och -satserna WriteLine i föregående exempel.

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

Ett kompilatorfel rapporteras för försöket att lägga till ett heltal och ett objekt i uttrycket obj + 3. Inget fel rapporteras dock för dyn + 3. Uttrycket som innehåller dyn kontrolleras inte vid kompileringstillfället eftersom typen av dyn är dynamic.

I följande exempel används dynamic i flera deklarationer. Metoden Main kontrasterar även kompileringstidstypkontroll med körningstypkontroll.

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

Språkspecifikation för C#

Mer information finns i följande avsnitt i C#-språkspecifikationen:

Se även