Share via


Panoramica del linguaggio C#

Il linguaggio C# è il più diffuso per la piattaforma .NET, un ambiente di sviluppo open source, gratuito e multipiattaforma. I programmi C# possono essere eseguiti in molti dispositivi diversi: da quelli IoT (Internet delle cose) al cloud e in tutti i dispositivi e le modalità esistenti fra queste due tipologie. È possibile scrivere app per cellulari, computer desktop, computer portatili e server.

C# è un linguaggio per utilizzo generico multipiattaforma che migliora la produttività degli sviluppatori durante la scrittura di codice ad alte prestazioni. Con i suoi milioni di sviluppatori, C# è il linguaggio .NET più diffuso. C# offre un ampio supporto nell'ecosistema e in tutti i carichi di lavoro .NET. In base ai principi orientati agli oggetti, incorpora molte funzionalità di altri paradigmi, non ultima la programmazione funzionale. Le funzionalità di basso livello supportano scenari ad alta efficienza senza scrivere codice non gestito. La maggior parte del runtime e delle librerie .NET è scritta in C# e i progressi in C# spesso offrono vantaggi a tutti gli sviluppatori .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#:

// This line prints "Hello, World" 
Console.WriteLine("Hello, World");

La riga che inizia con // è un commento a riga singola. I commenti a riga singola di C# iniziano con // e continuano fino alla fine della riga corrente. C# supporta anche commenti a più righe. I commenti a più righe iniziano con /* e terminano con */. Il metodo WriteLine della classe Console, che si trova nello spazio dei nomi System, produce l’output del programma. Questa classe viene fornita dalle librerie di classi standard a cui, per impostazione predefinita, viene fatto automaticamente riferimento in ogni programma C#.

L’esempio precedente mostra una forma del programma “Hello, World”, usando istruzioni di primo livello. Le versioni precedenti di C# richiedevano di definire il punto di ingresso del programma in un metodo. Questo formato è ancora valido ed è riscontrabile in molti esempi di C# esistenti. È anche necessario avere familiarità con questa forma, come illustrato nell’esempio seguente:

using System;

class Hello
{
    static void Main()
    {
        // This line prints "Hello, World" 
        Console.WriteLine("Hello, World");
    }
}

Questa versione mostra i blocchi predefiniti usati nei programmi. 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 spazio dei nomi System contiene molti tipi, come la classe Console, a cui viene fatto riferimento nel programma e molti 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. Nell’esempio precedente, lo spazio dei nomi era incluso in modo implicito.

La classe Hello dichiarata dal programma "Hello World" ha un solo membro, ovvero il metodo denominato Main. Il metodo Main 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, quando non sono presenti istruzioni di primo livello, un metodo statico chiamato Main funge da punto di ingresso di un programma C#.

Entrambe le forme del punto di ingresso producono codice equivalente. Quando si usano istruzioni di primo livello, il compilatore sintetizza la classe e il metodo contenuti per il punto di ingresso del programma.

Suggerimento

Gli esempi in questo articolo offrono un primo sguardo al codice C#. Alcuni esempi possono mostrare elementi di C# con cui non si ha familiarità. Quando si è pronti per apprendere C#, iniziare con le esercitazioni per principianti oppure approfondire i collegamenti in ogni sezione. Se si ha esperienza con Java, JavaScript, TypeScript o Python, leggere i suggerimenti per trovare rapidamente le informazioni necessarie per apprendere rapidamente C#.

Funzionalità di C# familiari

C# è accessibile ai principianti ma offre funzionalità avanzate per sviluppatori esperti che scrivono applicazioni specializzate. È possibile essere produttivi rapidamente. È possibile apprendere tecniche più specializzate in base alle esigenze delle applicazioni.

Le app C# traggono vantaggio dalla gestione automatica della memoria del runtime di .NET. Le app C# usano anche le librerie di runtime estese fornite dall’SDK .NET. Alcuni componenti sono indipendenti dalla piattaforma, ad esempio librerie di file system, raccolte di dati e librerie matematiche. Altri sono specifici di un singolo carico di lavoro, ad esempio le librerie Web di ASP.NET Core o la libreria dell’interfaccia utente .NET MAUI. Un ecosistema open source avanzato in NuGet augmenta le librerie che fanno parte del runtime. Queste librerie forniscono ancora più componenti che è possibile usare.

C# fa parte della famiglia di linguaggi C. La sintassi di C# si presenta familiare se sono stati usati C, C++, JavaScript o Java. Analogamente a tutti i linguaggi della famiglia C, i punti e virgola (;) definiscono la fine delle istruzioni. Gli identificatori di C# fanno distinzione tra maiuscole e minuscole. C# usa le stesse parentesi graffe, { e }, istruzioni di controllo, come if, else e switch e costrutti di ciclo, come for e while. C# include anche un’istruzione foreach per qualsiasi tipo di raccolta.

C# è un linguaggio fortemente tipizzato. Ogni variabile dichiarata ha un tipo noto in fase di compilazione. Il compilatore o gli strumenti di modifica indicano se si usa il tipo in modo errato. È possibile correggere questi errori prima di eseguire il programma. Tipi di dati fondamentali sono incorporati nel linguaggio e nel runtime: tipi di valori come int, double, char, tipi di riferimenti come string, matrici e altre raccolte. Quando si scrivono i programmi, si creano tipi personalizzati. Questi tipi possono essere tipi struct per valori oppure tipi class che definiscono il comportamento orientato agli oggetti. È possibile aggiungere il modificatore record ai tipi struct o class in modo che il compilatore sintetizzi il codice per confronti di uguaglianza. È anche possibile creare definizioni interface, che definiscono un contratto o un insieme di membri, che un tipo che implementa tale interfaccia deve fornire. È anche possibile definire tipi e metodi generici. Quando usato, generics usa parametri di tipo per fornire un segnaposto per un tipo effettivo.

Quando si scrive codice, si definiscono funzioni, chiamate anche metodi, come membri dei tipi struct e class. Questi metodi definiscono il comportamento dei tipi. È possibile eseguire l’overload dei metodi con un numero o tipi diversi di parametri. I metodi possono facoltativamente restituire un valore. Oltre ai metodi, i tipi di C# possono avere proprietà, che sono elementi dati supportati da funzioni chiamate funzioni di accesso. I tipi di C# possono definire eventi, che consentono a un tipo di notificare ai sottoscrittori azioni importanti. C# supporta tecniche orientate agli oggetti, ad esempio ereditarietà e polimorfismo per i tipi class.

Le app C# usano eccezioni per segnalare e gestire errori. Se si usa C++ o Java, questa procedura risulterà familiare. Il codice genera un’eccezione quando non riesce a eseguire le operazioni previste. Altro codice, indipendentemente dal numero di livelli superiori dello stack di chiamate, può facoltativamente eseguire il ripristino usando un blocco try - catch.

Caratteristiche distintive di C#

Alcuni elementi di C# potrebbero essere meno familiari. LINQ (Language Integrated Query) fornisce una sintassi comune basata su criteri per eseguire query o trasformare qualsiasi raccolta di dati. LINQ unifica la sintassi per l’esecuzione di query su raccolte in memoria, dati strutturati come XML o JSON, archiviazione di database e persino API dati basate sul cloud. Una volta imparato un insieme di sintassi, sarà possibile cercare e modificare i dati indipendentemente dalla relativa risorsa di archiviazione. La query seguente trova tutti gli studenti la cui media dei punti di voto è maggiore di 3,5:

var honorRoll = from student in Students
                where student.GPA > 3.5
                select student;

La query precedente funziona per molti tipi di risorse di archiviazione rappresentati da Students. Potrebbe trattarsi di una raccolta di oggetti, una tabella di database, un BLOB di archiviazione cloud o una struttura XML. La stessa sintassi di query funziona per tutti i tipi di risorse di archiviazione.

Il modello di programmazione asincrona basato su attività consente di scrivere codice che viene letto come se venisse eseguito in modo sincrono, anche se viene eseguito in modo asincrono. Usa le parole chiave async e await per descrivere i metodi asincroni e quando un’espressione viene valutata in modo asincrono. L’esempio seguente attende una richiesta Web asincrona. Al termine dell’operazione asincrona, il metodo restituisce la lunghezza della risposta:

public static async Task<int> GetPageLengthAsync(string endpoint)
{
    var client = new HttpClient();
    var uri = new Uri(endpoint);
    byte[] content = await client.GetByteArrayAsync(uri);
    return content.Length;
}

C# supporta anche un’istruzione await foreach per eseguire l’iterazione di una raccolta supportata da un’operazione asincrona, ad esempio un’API di paging GraphQL. L’esempio seguente legge i dati in blocchi, restituendo un iteratore che fornisce l’accesso a ogni elemento quando è disponibile:

public static async IAsyncEnumerable<int> ReadSequence()
{
    int index = 0;
    while (index < 100)
    {
        int[] nextChunk = await GetNextChunk(index);
        if (nextChunk.Length == 0)
        {
            yield break;
        }
        foreach (var item in nextChunk)
        {
            yield return item;
        }
        index++;
    }
}

I chiamanti possono eseguire l’iterazione della raccolta usando un’istruzione await foreach:

await foreach (var number in ReadSequence())
{
    Console.WriteLine(number);
}

C# fornisce i criteri di ricerca. Tali espressioni consentono di esaminare i dati e prendere decisioni in base alle relative caratteristiche. I criteri di ricerca offrono una sintassi ottimale per il flusso di controllo basato sui dati. Il codice seguente illustra in che modo i metodi per le operazioni booleane and, or e xor possono essere espresse usando la sintassi dei criteri di ricerca:

public static bool Or(bool left, bool right) =>
    (left, right) switch
    {
        (true, true) => true,
        (true, false) => true,
        (false, true) => true,
        (false, false) => false,
    };

public static bool And(bool left, bool right) =>
    (left, right) switch
    {
        (true, true) => true,
        (true, false) => false,
        (false, true) => false,
        (false, false) => false,
    };
public static bool Xor(bool left, bool right) =>
    (left, right) switch
    {
        (true, true) => false,
        (true, false) => true,
        (false, true) => true,
        (false, false) => false,
    };

Le espressioni dei criteri di ricerca possono essere semplificate usando _ come catch all per qualsiasi valore. L’esempio seguente illustra come semplificare il metodo and:

public static bool ReducedAnd(bool left, bool right) =>
    (left, right) switch
    {
        (true, true) => true,
        (_, _) => false,
    };

Infine, come parte dell’ecosistema .NET, è possibile usare Visual Studio o Visual Studio Code con C# DevKit. Questi strumenti permettono una comprensione approfondita di C#, incluso il codice che viene scritto. Forniscono anche funzionalità di debug.