Presentazione del linguaggio C#
C# (pronunciato "See Sharp") è un linguaggio di programmazione moderno, orientato a oggetti e indipendente dai tipi. C# consente agli sviluppatori di compilare molti tipi di applicazioni sicure e affidabili eseguite in .NET. C# ha le sue radici nella famiglia di linguaggi C e risulterà immediatamente familiare ai programmatori di C, C++, Java e JavaScript. Questa presentazione offre una panoramica dei componenti principali del linguaggio in C# 8 e versioni precedenti. Se si vuole esplorare il linguaggio tramite esempi interattivi, provare l'introduzione alle esercitazioni su C #.
C# è un linguaggio di programmazione orientato a oggetti e orientato ai componenti. C# fornisce costrutti di linguaggio per supportare direttamente questi concetti, rendendo C# un linguaggio naturale in cui creare e usare componenti software. Dalla sua origine, C# ha aggiunto funzionalità per supportare nuovi carichi di lavoro e procedure di progettazione software emergenti. Alla base, C# è un linguaggio orientato a oggetti. È possibile definire i tipi e il relativo comportamento.
Diverse funzionalità C# consentono di creare applicazioni affidabili e durevoli. Garbage Collection recupera automaticamente la memoria occupata da oggetti inutilizzati non raggiungibili. I tipi nullable sono sorvegliati dalle variabili che non fanno riferimento a oggetti allocati. La gestione delle eccezioni offre un approccio strutturato ed estendibile al rilevamento e al ripristino degli errori. Le espressioni lambda supportano tecniche di programmazione funzionale. La sintassi LINQ (Language Integrated Query) crea un modello comune per l'uso dei dati da qualsiasi origine. Il supporto del linguaggio per le operazioni asincrone fornisce la sintassi per la compilazione di sistemi distribuiti. C# ha un sistema di tipi unificato. Tutti i tipi C#, inclusi i tipi di primitiva quali int
e double
, ereditano da un unico tipo object
radice. Tutti i tipi condividono un set di operazioni comuni. I valori di qualsiasi tipo possono essere archiviati, trasportati e gestiti in modo coerente. C# supporta inoltre sia i tipi riferimento definiti dall'utente che i tipi valore. C# consente l'allocazione dinamica di oggetti e l'archiviazione in linea di strutture leggere. C# supporta tipi e metodi generici, che offrono maggiore sicurezza dei tipi e prestazioni. C# fornisce iteratori che consentono agli implementatori di classi di raccolta di definire comportamenti personalizzati per il codice client.
C# enfatizza il controllo delle versioni per garantire che i programmi e le librerie possano evolversi nel tempo in modo compatibile. Gli aspetti della progettazione di C# influenzati direttamente dalle considerazioni sul controllo delle versioni includono i modificatori e separati, le regole per la risoluzione dell'overload virtual
override
del metodo e il supporto per dichiarazioni esplicite dei membri di interfaccia.
Architettura .NET
I programmi C# vengono eseguiti in .NET, un sistema di esecuzione virtuale denominato Common Language Runtime (CLR) e un set di librerie di classi. CLR è l'implementazione da parte di Microsoft dell'interfaccia della riga di comando, uno standard internazionale. L'interfaccia della riga di comando è la base per la creazione di ambienti di esecuzione e sviluppo in cui linguaggi e librerie si integrano senza problemi.
Il codice sorgente scritto in C# viene compilato in un linguaggio intermedio conforme alla specifica dell'interfaccia della riga di comando. Il codice IL e le risorse, ad esempio bitmap e stringhe, vengono archiviati in un assembly, in genere con un'estensione .dll. Un assembly contiene un manifesto che fornisce informazioni su tipi, versione e impostazioni cultura dell'assembly.
Quando viene eseguito il programma C#, l'assembly viene caricato in CLR. CLR esegue la compilazione JIT per convertire il codice IL in istruzioni del computer nativo. CLR fornisce altri servizi correlati all'operazione di Garbage Collection automatica, alla gestione delle eccezioni e alla gestione delle risorse. Il codice eseguito da CLR viene talvolta definito "codice gestito". Il "codice non gestito" viene compilato nel linguaggio macchina nativo destinato a una piattaforma specifica.
L'interoperabilità del linguaggio è una funzionalità chiave di .NET. Il codice IL prodotto dal compilatore C# è conforme a Common Type Specification (CTS). Il codice IL generato da C# può interagire con il codice generato dalle versioni .NET di F#, Visual Basic, C++. Sono disponibili più di 20 altri linguaggi conformi a CTS. Un singolo assembly può contenere più moduli scritti in linguaggi .NET diversi. I tipi possono fare riferimento tra loro come se fossero scritti nello stesso linguaggio.
Oltre ai servizi in fase di esecuzione, .NET include anche librerie estese. Queste librerie supportano molti carichi di lavoro diversi. Sono organizzati in spazi dei nomi che forniscono un'ampia gamma di funzionalità utili. Le librerie includono tutto, dall'input e dall'output del file alla manipolazione di stringhe all'analisi XML, ai framework dell'applicazione Web Windows form. La tipica applicazione C# usa ampiamente la libreria di classi .NET per gestire le attività comuni di "plumbing".
Per altre informazioni su .NET, vedere Panoramica di .NET.
Hello world
Il programma "Hello World" viene tradizionalmente usato per presentare un linguaggio di programmazione. Di seguito è riportato il programma Hello, World in C#:
using System;
class Hello
{
static void Main()
{
Console.WriteLine("Hello, World");
}
}
Il programma "Hello World" inizia con una direttiva using
che fa riferimento allo spazio dei nomi System
. Gli spazi dei nomi consentono di organizzare i programmi e le librerie C# in modo gerarchico. Gli spazi dei nomi contengono tipi e altri spazi dei nomi. Lo stazio dei nomi System
, ad esempio, contiene diversi tipi, come la classe Console
a cui viene fatto riferimento nel programma, e altri spazi dei nomi, come IO
e Collections
. Una direttiva using
che fa riferimento a un determinato spazio dei nomi consente l'uso non qualificato dei tipi che sono membri di tale spazio dei nomi. Grazie alla direttiva using
, il programma può usare Console.WriteLine
come sintassi abbreviata per System.Console.WriteLine
.
La classe Hello
dichiarata dal programma "Hello World" ha un solo membro, ovvero il metodo denominato Main
. Il Main
metodo viene dichiarato con il modificatore static
. Mentre i metodi di istanza possono fare riferimento a una particolare istanza dell'oggetto contenitore usando la parola chiave this
, i metodi statici operano senza riferimento a un determinato oggetto. Per convenzione, un metodo statico denominato Main
funge da punto di ingresso di un programma C#.
L'output del programma viene prodotto dal metodo WriteLine
della classe Console
nello spazio dei nomi System
. Questa classe viene fornita da librerie di classi standard a cui, per impostazione predefinita, fa automaticamente riferimento il compilatore.
Tipi e variabili
Un tipo definisce la struttura e il comportamento di tutti i dati in C#. La dichiarazione di un tipo può includere i membri, il tipo di base, le interfacce implementate e le operazioni consentite per tale tipo. Una variabile è un'etichetta che fa riferimento a un'istanza di un tipo specifico.
In C# esistono due generi di tipi: tipi valore e tipi riferimento. Le variabili dei tipi valore contengono direttamente i dati. Le variabili dei tipi riferimento archiviano i riferimenti ai dati, questi ultimi sono noti come oggetti. Con i tipi riferimento, è possibile che due variabili fanno riferimento allo stesso oggetto e che le operazioni su una variabile influiscano sull'oggetto a cui fa riferimento l'altra variabile. Con i tipi valore, le variabili hanno ognuna una propria copia dei dati e non è possibile che le operazioni su uno influiscano sull'altra ( ref
ad eccezione delle variabili di out
parametro e ).
Un identificatore è un nome di variabile. Un identificatore è una sequenza di caratteri Unicode senza spazi vuoti. Un identificatore può essere una parola riservata C#, se è preceduta da @
. L'uso di una parola riservata come identificatore può essere utile quando si interagisce con altre lingue.
I tipi valore di C# sono ulteriormente suddivisi in tipi semplici, tipienum, tipi struct, tipi valore nullable e tipi di valore tupla. I tipi riferimento di C# sono ulteriormente suddivisi in tipi di classe, tipi di interfaccia, tipi di matrice e tipi delegati.
La struttura seguente offre una panoramica del sistema di tipi di C#.
- Tipi valore
- Tipi semplici
- Integrale con segno: ,
short
,int
,long
- Integrale senza segno: ,
ushort
,uint
,ulong
- Caratteri Unicode: , che rappresenta un'unità di codice UTF-16
- Virgola mobile binaria IEEE: ,
double
- Virgola mobile decimale a precisione elevata:
- Boolean:
bool
, che rappresenta valori booleani, ovvero valori che sonotrue
ofalse
- Integrale con segno: ,
- Tipi enum
- Tipi definiti dall'utente nel formato
enum E {...}
. Un tipoenum
è un tipo distinto con costanti denominate. Ogni tipoenum
ha un tipo sottostante, che deve essere uno degli otto tipi integrali. Il set di valori di un tipoenum
coincide con il set di valori del tipo sottostante.
- Tipi definiti dall'utente nel formato
- Tipi struct
- Tipi definiti dall'utente nel formato
struct S {...}
- Tipi definiti dall'utente nel formato
- Tipi valore nullable
- Estensioni di tutti gli altri tipi valore con un valore
null
- Estensioni di tutti gli altri tipi valore con un valore
- Tipi valore di tupla
- Tipi definiti dall'utente nel formato
(T1, T2, ...)
- Tipi definiti dall'utente nel formato
- Tipi semplici
- Tipi riferimento
- Tipi di classe
- Classe di base principale di tutti gli altri tipi:
object
- Stringhe Unicode: , che rappresenta una sequenza di unità di codice UTF-16
- Tipi definiti dall'utente nel formato
class C {...}
- Classe di base principale di tutti gli altri tipi:
- Tipi di interfaccia
- Tipi definiti dall'utente nel formato
interface I {...}
- Tipi definiti dall'utente nel formato
- Tipi di matrice
- Unidimensionale, multidimensionale e irregolare. Ad esempio:
int[]
,int[,]
eint[][]
- Unidimensionale, multidimensionale e irregolare. Ad esempio:
- Tipi delegato
- Tipi definiti dall'utente nel formato
delegate int D(...)
- Tipi definiti dall'utente nel formato
- Tipi di classe
I programmi C# usano le dichiarazioni di tipo per creare nuovi tipi. Una dichiarazione di tipo consente di specificare il nome e i membri del nuovo tipo. Sei categorie di tipi di C# sono definibili dall'utente: tipi classe, tipi struct, tipi di interfaccia, tipi enum, tipi delegati e tipi valore di tupla. È anche possibile dichiarare record
tipi, record struct
o record class
. I tipi di record hanno membri sintetizzati dal compilatore. I record vengono utilizzati principalmente per archiviare i valori, con un comportamento minimo associato.
- Un tipo
class
definisce una struttura dati contenente membri dati (campi) e membri funzione (metodi, proprietà e altro). I tipi classe supportano l'ereditarietà singola e il polimorfismo, meccanismi in base ai quali le classi derivate possono estendere e specializzare le classi di base. - Un tipo
struct
è simile a un tipo classe in quanto rappresenta una struttura con membri dati e membri funzione. Tuttavia, a differenza delle classi, gli struct sono tipi valore e in genere non richiedono l'allocazione dell'heap. I tipi struct non supportano l'ereditarietà specificata dall'utente e tutti i tipi struct ereditano in modo implicito dal tipoobject
. - Un
interface
tipo definisce un contratto come set denominato di membri pubblici. Unclass
oggettostruct
o che implementa uninterface
oggetto deve fornire implementazioni dei membri dell'interfaccia. Un tipointerface
può ereditare da più interfacce di base e un tipoclass
ostruct
può implementare più interfacce. - Un tipo
delegate
rappresenta i riferimenti ai metodi, con un elenco di parametri e un tipo restituito particolari. I delegati consentono di trattare i metodi come entità che è possibile assegnare a variabili e passare come parametri. I delegati sono analoghi ai tipi funzione forniti dai linguaggi funzionali. Sono anche simili al concetto di puntatori a funzione presenti in altri linguaggi. A differenza dei puntatori a funzione, i delegati sono orientati a oggetti e indipendente dai tipi.
I class
tipi struct
, interface
, e supportano delegate
tutti i generics, in base ai quali possono essere parametrizzati con altri tipi.
C# supporta matrici unidimensionali e multidimensionali di qualsiasi tipo. A differenza dei tipi elencati in precedenza, i tipi di matrice non devono essere dichiarati prima di poter essere usati. Al contrario, i tipi matrice vengono costruiti facendo seguire a un nome di tipo delle parentesi quadre. Ad esempio, int[]
int
è una matrice unidimensionale di , int[,]
int
è una matrice bidimensionale di e int[][]
è una matrice unidimensionale di matrici unidimensionali, o una matrice "irregolare", di int
.
I tipi nullable non richiedono una definizione separata. Per ogni tipo non nullable T
è presente un tipo nullable corrispondente T?
, che può contenere un valore aggiuntivo, null
. Ad esempio, int?
è un tipo che può contenere qualsiasi integer a 32 bit null
o il valore e string?
è un tipo che può contenere string
qualsiasi o il valore null
.
Il sistema di tipi di C# è unificato in modo che un valore di qualsiasi tipo possa essere considerato come object
. In C# ogni tipo deriva direttamente o indirettamente dal tipo classe object
e object
è la classe di base principale di tutti i tipi. I valori dei tipi riferimento vengono trattati come oggetti semplicemente visualizzando tali valori come tipi object
. I valori dei tipi valore vengono trattati come oggetti mediante l'esecuzione di operazioni di boxing e unboxing. Nell'esempio seguente un valore int
viene convertito in object
e quindi convertito nuovamente in int
.
int i = 123;
object o = i; // Boxing
int j = (int)o; // Unboxing
Quando un valore di un tipo valore viene assegnato a un object
riferimento, viene allocata una "casella" per contenere il valore. Tale casella è un'istanza di un tipo riferimento e il valore viene copiato in tale casella. Viceversa, quando si esegue il object
cast di un riferimento a un tipo valore, object
viene eseguito un controllo che l'oggetto a cui si fa riferimento sia una casella del tipo valore corretto. Se il controllo ha esito positivo, il valore nella casella viene copiato nel tipo di valore.
Il sistema di tipi unificato di C# significa che i tipi valore vengono considerati come riferimenti object
"su richiesta". A causa dell'unificazione, le librerie per utilizzo object
object
generico che usano il tipo possono essere usate con tutti i tipi che derivano da , inclusi i tipi riferimento e i tipi valore.
In C# sono disponibili diversi tipi di variabili, inclusi campi, elementi matrice, variabili locali e parametri. Le variabili rappresentano posizioni di archiviazione. Ogni variabile ha un tipo che determina quali valori possono essere archiviati nella variabile, come illustrato di seguito.
- Tipo valore non-nullable
- Valore esattamente del tipo indicato
- Tipo valore nullable
- Valore
null
o valore esattamente del tipo indicato
- Valore
- object
- Riferimento
null
, riferimento a un oggetto di qualsiasi tipo riferimento oppure riferimento a un valore boxed di qualsiasi tipo valore
- Riferimento
- Tipo classe
- Riferimento
null
, riferimento a un'istanza del tipo classe oppure riferimento a un'istanza di una classe derivata dal tipo classe
- Riferimento
- Tipo interfaccia
- Riferimento
null
, riferimento a un'istanza di un tipo classe che implementa il tipo interfaccia oppure riferimento a un valore boxed di un tipo valore che implementa il tipo interfaccia
- Riferimento
- Tipo matrice
- Riferimento
null
, riferimento a un'istanza del tipo matrice oppure riferimento a un'istanza di un tipo matrice compatibile
- Riferimento
- Tipo delegato
- Riferimento
null
oppure riferimento a un'istanza di un tipo delegato compatibile
- Riferimento
Struttura del programma
I concetti organizzativi principali in C# sono programmi, spazi dei nomi, tipi, membrie assembly. I programmi dichiarano i tipi, che contengono i membri e possono essere organizzati in spazi dei nomi. Classi, struct e interfacce sono esempi di tipi. I campi, i metodi, le proprietà e gli eventi sono esempi di membri. Quando i programmi C# vengono compilati, vengono fisicamente suddivisi in assembly. Gli assembly hanno in genere l'estensione di .exe
file o .dll
, a seconda che .exe
rispettivamente applicazioni o librerie.
Come piccolo esempio, si consideri un assembly che contiene il codice seguente:
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;
}
}
}
Il nome completo di questa classe è Acme.Collections.Stack
. La classe contiene vari membri: un campo _top
, due metodi Push
e Pop
e una classe annidata Entry
. La Entry
classe contiene inoltre tre membri: una proprietà denominata Next
, una proprietà denominata Data
e un costruttore. è Stack
una Stack
. Ha un parametro di tipo, T
che viene sostituito con un tipo concreto quando viene usato.
Uno stack è una raccolta "first in - last out". I nuovi elementi vengono aggiunti all'inizio dello stack. Quando un elemento viene rimosso, viene rimosso dall'inizio dello stack. Nell'esempio precedente viene dichiarato il Stack
tipo che definisce l'archiviazione e il comportamento per uno stack. È possibile dichiarare una variabile che fa riferimento a un'istanza del tipo Stack
per usare tale funzionalità.
Gli assembly contengono codice eseguibile sotto forma di istruzioni di linguaggio intermedio (Intermediate Language, IL) e informazioni simboliche sotto forma di metadati. Prima dell'esecuzione, il compilatore JIT (Just-In-Time) di .NET Common Language Runtime converte il codice IL in un assembly in codice specifico del processore.
Poiché un assembly è un'unità #include
di funzionalità autodescritta contenente sia il codice che i metadati, non è necessario utilizzare direttive e file di intestazione in C#. I membri e i tipi pubblici contenuti in un determinato assembly vengono resi disponibili in un programma C# semplicemente facendo riferimento a tale assembly durante la compilazione del programma. Questo programma usa ad esempio la classe Acme.Collections.Stack
dell'assembly acme.dll
:
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
}
}
Per compilare questo programma, è necessario fare riferimento all'assembly contenente la classe stack definita nell'esempio precedente.
I programmi C# possono essere archiviati in diversi file di origine. Quando viene compilato un programma C#, tutti i file di origine vengono elaborati insieme e i file di origine possono fare liberamente riferimento l'uno all'altro. Concettualmente, è come se tutti i file di origine fossero concatenati in un unico file di grandi dimensioni prima dell'elaborazione. Le dichiarazioni con inoltro non sono mai necessarie in C# perché, con poche eccezioni, l'ordine delle dichiarazioni non è significativo. C# non limita un file di origine alla dichiarazione di un solo tipo pubblico né richiede che il nome del file di origine corrisponda a un tipo dichiarato nel file di origine.
Altri articoli di questa presentazione illustrano questi blocchi organizzativi.
Commenti e suggerimenti
https://aka.ms/ContentUserFeedback.
Presto disponibile: Nel corso del 2024 verranno gradualmente disattivati i problemi di GitHub come meccanismo di feedback per il contenuto e ciò verrà sostituito con un nuovo sistema di feedback. Per altre informazioni, vedereInvia e visualizza il feedback per