En rundtur i C#-språket
C# (uttalas "See Sharp") är ett modernt, objektorienterat och typsäkert programmeringsspråk. Med C# kan utvecklare skapa många typer av säkra och robusta program som körs i .NET. C# har sina rötter i C-språkfamiljen och kommer omedelbart att känna till C-, C++-, Java- och JavaScript-programmerare. Den här rundturen ger en översikt över de viktigaste komponenterna i språket i C# 8 och tidigare. Om du vill utforska språket med hjälp av interaktiva exempel kan du prova introduktionen till C# -självstudier.
C# är ett objektorienterat, komponentorienterat programmeringsspråk. C# tillhandahåller språkkonstruktioner som direkt stöder dessa begrepp, vilket gör C# till ett naturligt språk där du kan skapa och använda programvarukomponenter. Sedan dess ursprung har C# lagt till funktioner för att stödja nya arbetsbelastningar och nya metoder för programvarudesign. I grunden är C# ett objektorienterat språk. Du definierar typer och deras beteende.
Flera C#-funktioner hjälper dig att skapa robusta och hållbara program. Skräpinsamling återtar automatiskt minne som används av oanvända objekt som inte kan nås. Nullbara typer skyddar mot variabler som inte refererar till allokerade objekt. Undantagshantering ger en strukturerad och utökningsbar metod för felidentifiering och återställning. Lambda-uttryck stöder funktionella programmeringstekniker . LINQ-syntaxen (Language Integrated Query) skapar ett vanligt mönster för att arbeta med data från alla källor. Språkstöd för asynkrona åtgärder ger syntax för att skapa distribuerade system. C# har ett enhetligt typsystem. Alla C#-typer, inklusive primitiva typer som int
och double
, ärver från en enda rottyp object
. Alla typer delar en uppsättning vanliga åtgärder. Värden av alla typer kan lagras, transporteras och användas på ett konsekvent sätt. Dessutom stöder C# både användardefinierade referenstyper och värdetyper. C# möjliggör dynamisk allokering av objekt och inbyggd lagring av lätta strukturer. C# stöder allmänna metoder och typer, vilket ger ökad typsäkerhet och prestanda. C# tillhandahåller iteratorer som gör det möjligt för implementerare av samlingsklasser att definiera anpassade beteenden för klientkod.
C# betonar versionshantering för att säkerställa att program och bibliotek kan utvecklas över tid på ett kompatibelt sätt. Aspekter av C#:s design som påverkades direkt av versionsöverväganden omfattar separata virtual
och override
modifierare, regler för metodöverbelastningsmatchning och stöd för explicita medlemsdeklarationer för gränssnitt.
.NET-arkitektur
C#-program körs på .NET, ett virtuellt körningssystem som kallas common language runtime (CLR) och en uppsättning klassbibliotek. CLR är microsofts implementering av den gemensamma språkinfrastrukturen (CLI), en internationell standard. CLI är grunden för att skapa körnings- och utvecklingsmiljöer där språk och bibliotek fungerar sömlöst tillsammans.
Källkoden som skrivits i C# kompileras till ett mellanliggande språk (IL) som överensstämmer med CLI-specifikationen. IL-koden och resurserna, till exempel bitmappar och strängar, lagras i en sammansättning, vanligtvis med ett tillägg av.dll. En sammansättning innehåller ett manifest som innehåller information om sammansättningens typer, version och kultur.
När C#-programmet körs läses sammansättningen in i CLR. CLR utför JIT-kompilering (Just-In-Time) för att konvertera IL-koden till interna datorinstruktioner. CLR tillhandahåller andra tjänster som rör automatisk skräpinsamling, undantagshantering och resurshantering. Kod som körs av CLR kallas ibland "hanterad kod". "Ohanterad kod" kompileras till ett internt datorspråk som riktar sig mot en specifik plattform.
Språksamverkans är en viktig funktion i .NET. IL-kod som produceras av C#-kompilatorn överensstämmer med CTS (Common Type Specification). IL-kod som genereras från C# kan interagera med kod som genererades från .NET-versionerna av F#, Visual Basic, C++. Det finns fler än 20 andra CTS-kompatibla språk. En enskild sammansättning kan innehålla flera moduler skrivna på olika .NET-språk. Typerna kan referera till varandra som om de hade skrivits på samma språk.
Förutom körningstjänster innehåller .NET även omfattande bibliotek. De här biblioteken stöder många olika arbetsbelastningar. De är ordnade i namnområden som ger en mängd användbara funktioner. Biblioteken omfattar allt från filindata och utdata till strängmanipulering till XML-parsning, till webbprogramramverk till Windows Forms kontroller. Det typiska C#-programmet använder .NET-klassbiblioteket i stor utsträckning för att hantera vanliga "VVS"-sysslor.
Mer information om .NET finns i Översikt över .NET.
Hello World
Programmet "Hello, World" används traditionellt för att introducera ett programmeringsspråk. Här är den i C#:
using System;
class Hello
{
static void Main()
{
Console.WriteLine("Hello, World");
}
}
Programmet "Hello, World" börjar med ett using
direktiv som refererar till System
namnområdet. Namnområden är ett hierarkiskt sätt att organisera C#-program och -bibliotek. Namnområden innehåller typer och andra namnområden, System
till exempel innehåller namnområdet ett antal typer, till exempel klassen Console
som refereras i programmet och ett antal andra namnområden, till exempel IO
och Collections
. Ett using
direktiv som refererar till ett givet namnområde möjliggör okvalificerad användning av de typer som är medlemmar i det namnområdet. På grund av using
direktivet kan programmet använda Console.WriteLine
som förkortning för System.Console.WriteLine
.
Klassen Hello
som deklareras av programmet "Hello, World" har en enda medlem, metoden med namnet Main
. Metoden Main
deklareras med static
modifieraren. Instansmetoder kan referera till en viss omslutande objektinstans med nyckelordet this
, men statiska metoder fungerar utan referens till ett visst objekt. Enligt konventionen fungerar en statisk metod med namnet Main
som startpunkt för ett C#-program.
Utdata från programmet skapas av WriteLine
-metoden för Console
klassen i System
namnområdet. Den här klassen tillhandahålls av standardklassbiblioteken, som som standard automatiskt refereras av kompilatorn.
Typer och variabler
En typ definierar strukturen och beteendet för alla data i C#. Deklarationen av en typ kan innehålla dess medlemmar, bastyp, gränssnitt som den implementerar och åtgärder som tillåts för den typen. En variabel är en etikett som refererar till en instans av en viss typ.
Det finns två typer av typer i C#: värdetyper och referenstyper. Variabler för värdetyper innehåller direkt sina data. Variabler för referenstyper lagrar referenser till sina data, där de senare kallas objekt. Med referenstyper är det möjligt för två variabler att referera till samma objekt och möjligt för åtgärder på en variabel att påverka objektet som refereras av den andra variabeln. Med värdetyper har variablerna var sin egen kopia av data och det är inte möjligt för åtgärder på den ena att påverka den andra (förutom ref
out
parametervariabler).
En identifierare är ett variabelnamn. En identifierare är en sekvens med unicode-tecken utan blanksteg. En identifierare kan vara ett reserverat C#-ord om det är prefixet .@
Att använda ett reserverat ord som identifierare kan vara användbart när du interagerar med andra språk.
C#:s värdetyper är ytterligare indelade i enkla typer, uppräkningstyper, structtyper, nullbara värdetyper och tuppelns värdetyper. C#:s referenstyper är ytterligare indelade i klasstyper, gränssnittstyper, matristyper och ombudstyper.
Följande disposition ger en översikt över C#:s typsystem.
- Värdetyper
- Enkla typer
- Signerad integral:
sbyte
,short
,int
,long
- Osignerad integral:
byte
,ushort
,uint
,ulong
- Unicode-tecken:
char
, som representerar en UTF-16-kodenhet - IEEE binär flyttalspunkt:
float
,double
- Decimaltecken med hög precision:
decimal
- Booleskt:
bool
, som representerar booleska värden – värden som är antingentrue
ellerfalse
- Signerad integral:
- Uppräkningstyper
- Användardefinierade typer av formuläret
enum E {...}
. Enenum
typ är en distinkt typ med namngivna konstanter. Varjeenum
typ har en underliggande typ, som måste vara en av de åtta integraltyperna. Uppsättningen med värden för enenum
typ är samma som uppsättningen med värden för den underliggande typen.
- Användardefinierade typer av formuläret
- Struct-typer
- Användardefinierade typer av formuläret
struct S {...}
- Användardefinierade typer av formuläret
- Typer av null-värden
- Tillägg för alla andra värdetyper med ett
null
värde
- Tillägg för alla andra värdetyper med ett
- Tuppelns värdetyper
- Användardefinierade typer av formuläret
(T1, T2, ...)
- Användardefinierade typer av formuläret
- Enkla typer
- Referenstyper
- Klasstyper
- Ultimat basklass av alla andra typer:
object
- Unicode-strängar:
string
, som representerar en sekvens med UTF-16-kodenheter - Användardefinierade typer av formuläret
class C {...}
- Ultimat basklass av alla andra typer:
- Gränssnittstyper
- Användardefinierade typer av formuläret
interface I {...}
- Användardefinierade typer av formuläret
- Matristyper
- Endimensionell, flerdimensionell och ojämn. Till exempel:
int[]
,int[,]
ochint[][]
- Endimensionell, flerdimensionell och ojämn. Till exempel:
- Delegera typer
- Användardefinierade typer av formuläret
delegate int D(...)
- Användardefinierade typer av formuläret
- Klasstyper
C#-program använder typdeklarationer för att skapa nya typer. En typdeklaration anger namnet och medlemmarna av den nya typen. Sex av C#:s kategorier av typer är användardefinierbara: klasstyper, structtyper, gränssnittstyper, uppräkningstyper, ombudstyper och tuppelns värdetyper. Du kan också deklarera record
typer, antingen record struct
, eller record class
. Posttyper har kompilatorsyntiserade medlemmar. Du använder poster främst för att lagra värden, med minimalt associerat beteende.
- En
class
typ definierar en datastruktur som innehåller datamedlemmar (fält) och funktionsmedlemmar (metoder, egenskaper och andra). Klasstyper stöder enkel arvs- och polymorfism, mekanismer där härledda klasser kan utöka och specialisera basklasser. - En
struct
typ liknar en klasstyp eftersom den representerar en struktur med datamedlemmar och funktionsmedlemmar. Men till skillnad från klasser är structs värdetyper och kräver vanligtvis inte heap-allokering. Struct-typer stöder inte användardefinierad arv och alla struct-typer ärver implicit från typenobject
. - En
interface
typ definierar ett kontrakt som en namngiven uppsättning offentliga medlemmar. Enclass
ellerstruct
som implementerar ettinterface
måste tillhandahålla implementeringar av gränssnittets medlemmar. Eninterface
kan ärva från flera basgränssnitt och enclass
ellerstruct
kan implementera flera gränssnitt. - En
delegate
typ representerar referenser till metoder med en viss parameterlista och returtyp. Ombud gör det möjligt att behandla metoder som entiteter som kan tilldelas till variabler och skickas som parametrar. Ombuden motsvarar funktionstyper som tillhandahålls av funktionella språk. De liknar också begreppet funktionspekare som finns på vissa andra språk. Till skillnad från funktionspekare är ombud objektorienterade och typsäkra.
Typerna class
, struct
, interface
och delegate
stöder alla generiska objekt, där de kan parametriseras med andra typer.
C# stöder endimensionella och flerdimensionella matriser av alla typer. Till skillnad från de typer som anges ovan behöver matristyper inte deklareras innan de kan användas. I stället skapas matristyper genom att följa ett typnamn med hakparenteser. Till exempel int[]
är en endimensionell matris med int
, int[,]
är en tvådimensionell matris med int
, och int[][]
är en endimensionell matris med endimensionella matriser, eller en "ojämn" matris, av int
.
Nullbara typer kräver ingen separat definition. För varje icke-nullbar typ T
finns det en motsvarande nullbar typ T?
, som kan innehålla ytterligare ett värde, null
. Är till exempel int?
en typ som kan innehålla valfritt 32-bitars heltal eller värde null
och string?
är en typ som kan innehålla valfritt string
eller värdet null
.
C#:s typsystem är enhetligt så att ett värde av alla typer kan behandlas som en object
. Varje typ i C# härleds direkt eller indirekt från object
klasstypen och object
är den ultimata basklassen av alla typer. Värden för referenstyper behandlas som objekt genom att bara visa värdena som typ object
. Värden för värdetyper behandlas som objekt genom att utföra boxnings- och avboxningsåtgärder. I följande exempel konverteras ett int
värde till object
och tillbaka igen till int
.
int i = 123;
object o = i; // Boxing
int j = (int)o; // Unboxing
När ett värde av en värdetyp tilldelas till en object
referens allokeras en "ruta" för att lagra värdet. Den rutan är en instans av en referenstyp och värdet kopieras till den rutan. När en object
referens konverteras till en värdetyp markeras däremot att den refererade object
är en ruta med rätt värdetyp. Om kontrollen lyckas kopieras värdet i rutan till värdetypen.
C#:s enhetliga typsystem innebär effektivt att värdetyper behandlas som object
referenser "på begäran". På grund av enandet kan allmänna bibliotek som använder typen object
användas med alla typer som härleds från object
, inklusive både referenstyper och värdetyper.
Det finns flera typer av variabler i C#, inklusive fält, matriselement, lokala variabler och parametrar. Variabler representerar lagringsplatser. Varje variabel har en typ som avgör vilka värden som kan lagras i variabeln, enligt nedan.
- Värdetyp som inte kan null
- Ett värde av den exakta typen
- Värdetyp som kan vara null
- Ett
null
värde eller ett värde av den exakta typen
- Ett
- objekt
- En
null
referens, en referens till ett objekt av valfri referenstyp eller en referens till ett boxat värde av valfri värdetyp
- En
- Klasstyp
- En
null
referens, en referens till en instans av den klasstypen eller en referens till en instans av en klass som härletts från den klasstypen
- En
- Gränssnittstyp
- En
null
referens, en referens till en instans av en klasstyp som implementerar den gränssnittstypen eller en referens till ett boxat värde av en värdetyp som implementerar den gränssnittstypen
- En
- Matristyp
- En
null
referens, en referens till en instans av den matristypen eller en referens till en instans av en kompatibel matristyp
- En
- Ombudstyp
- En
null
referens eller en referens till en instans av en kompatibel ombudstyp
- En
Programstruktur
De viktigaste organisationsbegreppen i C# är program, namnrymder, typer, medlemmar och sammansättningar. Program deklarerar typer som innehåller medlemmar och kan ordnas i namnområden. Klasser, structs och gränssnitt är exempel på typer. Fält, metoder, egenskaper och händelser är exempel på medlemmar. När C#-program kompileras paketeras de fysiskt i sammansättningar. Sammansättningar har vanligtvis filnamnstillägget .exe
eller .dll
, beroende på om de implementerarprogram eller bibliotek.
Som ett litet exempel bör du överväga en sammansättning som innehåller följande kod:
namespace Acme.Collections;
public class Stack<T>
{
Entry _top;
public void Push(T data)
{
_top = new Entry(_top, data);
}
public T Pop()
{
if (_top == null)
{
throw new InvalidOperationException();
}
T result = _top.Data;
_top = _top.Next;
return result;
}
class Entry
{
public Entry Next { get; set; }
public T Data { get; set; }
public Entry(Entry next, T data)
{
Next = next;
Data = data;
}
}
}
Det fullständigt kvalificerade namnet på den här klassen är Acme.Collections.Stack
. Klassen innehåller flera medlemmar: ett fält med namnet _top
, två metoder med namnet Push
och Pop
, och en kapslad klass med namnet Entry
. Klassen Entry
innehåller ytterligare tre medlemmar: en egenskap med namnet Next
, en egenskap med namnet Data
och en konstruktor. Stack
är en allmän klass. Den har en typparameter som T
ersätts med en betongtyp när den används.
En stack är en FILO-samling (first in - last out). Nya element läggs till överst i stacken. När ett element tas bort tas det bort från stackens överkant. I föregående exempel deklareras den Stack
typ som definierar lagring och beteende för en stack. Du kan deklarera en variabel som refererar till en instans av typen för att använda den Stack
funktionen.
Sammansättningar innehåller körbar kod i form av instruktioner för mellanliggande språk (IL) och symbolisk information i form av metadata. Innan den körs konverterar JIT-kompilatorn (Just-In-Time) för .NET Common Language Runtime IL-koden i en sammansättning till processorspecifik kod.
Eftersom en sammansättning är en självbeskrivande funktionsenhet som innehåller både kod och metadata behöver #include
du inte ha direktiv och huvudfiler i C#. De offentliga typerna och medlemmarna som ingår i en viss sammansättning görs tillgängliga i ett C#-program genom att referera till sammansättningen när programmet kompileras. Det här programmet använder Acme.Collections.Stack
till exempel klassen från acme.dll
sammansättningen:
class Example
{
public static void Main()
{
var s = new Acme.Collections.Stack<int>();
s.Push(1); // stack contains 1
s.Push(10); // stack contains 1, 10
s.Push(100); // stack contains 1, 10, 100
Console.WriteLine(s.Pop()); // stack contains 1, 10
Console.WriteLine(s.Pop()); // stack contains 1
Console.WriteLine(s.Pop()); // stack is empty
}
}
För att kompilera det här programmet måste du referera till sammansättningen som innehåller stackklassen som definierades i det tidigare exemplet.
C#-program kan lagras i flera källfiler. När ett C#-program kompileras bearbetas alla källfiler tillsammans och källfilerna kan fritt referera till varandra. Konceptuellt är det som om alla källfiler sammanfogades till en stor fil innan de bearbetades. Framåtdeklarationer behövs aldrig i C# eftersom deklarationsordningen med få undantag är obetydlig. C# begränsar inte en källfil till att endast deklarera en offentlig typ och kräver inte heller namnet på källfilen för att matcha en typ som deklarerats i källfilen.
Ytterligare artiklar i den här rundturen förklarar dessa organisationsblock.
Feedback
https://aka.ms/ContentUserFeedback.
Kommer snart: Under hela 2024 kommer vi att fasa ut GitHub-problem som feedbackmekanism för innehåll och ersätta det med ett nytt feedbacksystem. Mer information finns i:Skicka och visa feedback för