Considerazioni su versione e aggiornamento per gli sviluppatori C#

La compatibilità è un obiettivo importante quando vengono aggiunte nuove funzionalità al linguaggio C#. Nella quasi totalità dei casi, il codice esistente può essere ricompilato con una nuova versione del compilatore senza alcun problema. Il team del runtime .NET ha anche l'obiettivo di garantire la compatibilità per le librerie aggiornate. In quasi tutti i casi, quando l'app viene avviata da un runtime aggiornato con librerie aggiornate, il comportamento è esattamente uguale a quello delle versioni precedenti.

La versione del linguaggio usata per compilare l'app corrisponde in genere al moniker framework di destinazione (TFM) di runtime a cui viene fatto riferimento nel progetto. Per altre informazioni sulla modifica della versione predefinita del linguaggio, vedere l'articolo intitolato Configurare la versione del linguaggio. Questo comportamento predefinito garantisce la massima compatibilità.

Quando vengono introdotte modifiche che causano un'interruzione, vengono classificate come:

  • Modifica che causa un'interruzione del file binario: questo tipo di modifica causa un comportamento diverso, inclusi possibili arresti anomali, nell'applicazione o nella libreria quando viene avviata usando un nuovo runtime. È necessario ricompilare l'app per incorporare queste modifiche. Il file binario esistente non funzionerà correttamente.
  • Modifica che causa un'interruzione del codice sorgente: questo tipo di modifica cambia il significato del codice sorgente. È necessario apportare modifiche al codice sorgente prima di compilare l'applicazione con la versione più recente del linguaggio. Il file binario esistente verrà eseguito correttamente con l'host e il runtime più recenti. Si noti che per la sintassi del linguaggio, una modifica che causa un'interruzione del codice sorgente è anche una modifica funzionale, come definito nelle modifiche che causano un'interruzione del runtime.

Quando una modifica che causa un'interruzione del file binario influisce sull'app, è necessario ricompilare l'app, ma non occorre modificare il codice sorgente. Quando una modifica che causa un'interruzione del codice sorgente influisce sull'app, il file binario esistente viene ancora eseguito correttamente negli ambienti con il runtime e le librerie aggiornati. Tuttavia, è necessario apportare modifiche al codice sorgente per ricompilarlo con la nuova versione del linguaggio e del runtime. Se una modifica causa un'interruzione sia del codice sorgente che del file binario, è necessario ricompilare l'applicazione con la versione più recente e aggiornare il codice sorgente.

Considerato che l'obiettivo del team del linguaggio C# e del team del runtime è quello di evitare modifiche che causano un'interruzione, l'aggiornamento dell'applicazione consiste in genere nell'aggiornare il TFM e ricompilare l'app. Tuttavia, per le librerie distribuite pubblicamente, è consigliabile valutare attentamente i criteri per i TFM supportati e le versioni del linguaggio supportate. Si supponga di creare una nuova libreria con le funzionalità disponibili nella versione più recente e di dover verificare che le app create con le versioni precedenti del compilatore siano in grado di usarla. In caso contrario, è possibile che si decida di aggiornare una libreria esistente quando molti utenti non hanno ancora eseguito l'aggiornamento delle versioni.

Introduzione di modifiche che causano un'interruzione nelle librerie

Quando si adottano nuove funzionalità del linguaggio nell'API pubblica della libreria, è consigliabile valutare se l'adozione della funzionalità introduce una modifica che causa un'interruzione del file binario o del codice sorgente per gli utenti della libreria. Le modifiche apportate all'implementazione interna che non compaiono nelle interfacce public o protected sono compatibili.

Nota

Se si usa System.Runtime.CompilerServices.InternalsVisibleToAttribute per consentire ai tipi di visualizzare i membri interni, i membri interni possono introdurre modifiche che causano un'interruzione.

Una modifica che causa un'interruzione del file binario richiede agli utenti di ricompilare il codice per usare la nuova versione. Si consideri ad esempio questo metodo pubblico:

public double CalculateSquare(double value) => value * value;

Se si aggiunge il modificatore in al metodo, si introduce una modifica che causa un'interruzione del file binario:

public double CalculateSquare(in double value) => value * value;

Gli utenti devono ricompilare qualsiasi applicazione che usi il metodo CalculateSquare affinché la nuova libreria funzioni correttamente.

Una modifica che causa un'interruzione del codice sorgente richiede agli utenti di modificare il codice prima di ricompilare. Si consideri, ad esempio, questo tipo:

public class Person
{
    public string FirstName { get; }
    public string LastName { get; }

    public Person(string firstName, string lastName) => (FirstName, LastName) = (firstName, lastName);

    // other details omitted
}

In una versione più recente si potrebbe voler sfruttare i membri sintetizzati generati per i tipi record. Si apporta la modifica seguente:

public record class Person(string FirstName, string LastName);

La modifica precedente richiede modifiche per qualsiasi tipo derivato da Person. Per tutte queste dichiarazioni è necessario aggiungere il modificatore record.

Impatto delle modifiche che causano un'interruzione

Quando si aggiunge una modifica che causa un'interruzione del file binario alla libreria, si impone la ricompilazione di tutti i progetti che usano la libreria. Tuttavia, non sono richieste modifiche del codice sorgente in tali progetti. Di conseguenza, l'impatto della modifica che causa un'interruzione è abbastanza ridotto per ogni progetto.

Quando si apporta una modifica che causa un'interruzione del codice sorgente alla libreria, sono richieste modifiche del codice sorgente di tutti i progetti per poter usare la nuova libreria. Se la modifica necessaria richiede nuove funzionalità del linguaggio, occorre forzare l'aggiornamento dei progetti alla stessa versione del linguaggio e dei TFM in uso. Viene richiesto più lavoro per gli utenti, probabilmente costringendoli anche a eseguire l'aggiornamento.

L'impatto di qualsiasi modifica che causa un'interruzione dipende dal numero di progetti che hanno una dipendenza dalla libreria. Se la libreria viene usata internamente da poche applicazioni, è possibile reagire a eventuali modifiche che causano un'interruzione in tutti i progetti interessati. Tuttavia, se la libreria viene scaricata pubblicamente, è necessario valutare il potenziale impatto e considerare possibili alternative:

  • Si potrebbero aggiungere nuove API parallele alle API esistenti.
  • È possibile prendere in considerazione compilazioni parallele per TFM diversi.
  • Si potrebbe considerare il multitargeting.