Supporto multipiattaforma

Il software .NET moderno supporta più sistemi operativi e dispositivi. È importante che le librerie open source .NET offrano supporto per il maggior numero possibile di sviluppatori, che stiano creando un sito Web ASP.NET ospitato in Azure o un gioco .NET in Unity.

Destinazioni .NET e .NET Standard

Le destinazioni .NET e .NET Standard sono il modo migliore per aggiungere il supporto multipiattaforma a una libreria .NET.

  • .NET Standard è una specifica delle API .NET disponibili in tutte le implementazioni .NET. Scegliendo come destinazione .NET Standard è possibile creare librerie vincolate all'uso delle API di una determinata versione di .NET Standard, che possono quindi essere usate da tutte le piattaforme che implementano tale versione di .NET Standard.
  • Le versioni di .NET dalla 6 alla 8 sono implementazioni di .NET. Ogni versione è un prodotto singolo con un set uniforme di funzionalità e API che possono essere usate per app desktop di Windows e app console multipiattaforma, servizi cloud e siti Web.

Per altre informazioni sul confronto tra .NET e .NET Standard, vedere .NET 5 e .NET Standard.

.NET Standard

La scelta di .NET o .NET Standard come destinazione e la corretta compilazione del progetto non garantiscono che la libreria verrà eseguita correttamente in tutte le piattaforme:

  • Le API specifiche della piattaforma avranno esito negativo nelle altre piattaforme. Ad esempio, l'esecuzione di Microsoft.Win32.Registry avverrà correttamente in Windows e genererà un'eccezione PlatformNotSupportedException in qualsiasi altro sistema operativo.
  • Le API possono avere comportamenti diversi. Ad esempio, le API reflection hanno caratteristiche di prestazioni diverse quando un'applicazione usa la compilazione Ahead Of Time in iOS o nella piattaforma UWP.

Suggerimento

Il team di .NET offre un analizzatore della compatibilità della piattaforma che consente di individuare i possibili problemi.

✔️ INIZIARE includendo una destinazione netstandard2.0.

La maggior parte delle librerie per utilizzo generico non necessita di API non comprese in .NET Standard 2.0. .NET standard 2.0 è supportato da tutte le piattaforme moderne ed è il modo consigliato per supportare più piattaforme con una sola destinazione. Se non è necessario supportare .NET Framework, è anche possibile scegliere come destinazione .NET Standard 2.1.

✔️ INCLUDERE come destinazione net6.0 o versione successiva se sono richieste nuove API introdotte in una versione moderna di .NET.

Le app .NET 6 e versione successiva possono usare netstandard2.0 come destinazione, quindi net6.0 non è necessaria. È consigliabile specificare in modo esplicito come destinazione net6.0, net7.0 o net8.0 quando si intende usare API .NET più recenti.

❌ EVITARE di includere netstandard1.x come destinazione.

.NET Standard 1.x è distribuito come set granulare di pacchetti NuGet, che crea un grafo delle dipendenze dei pacchetti di grandi dimensioni e obbliga a scaricare numerosi pacchetti durante la compilazione. Le implementazioni moderne di .NET supportano .NET Standard 2.0. Scegliere come destinazione .NET Standard 1.x solo se è necessaria una piattaforma di destinazione meno recente.

✔️ INCLUDERE netstandard2.0 come destinazione se è necessaria una destinazione netstandard1.x.

Tutte le piattaforme che supportano .NET Standard 2.0 useranno la destinazione netstandard2.0 con il vantaggio di avere un grafo dei pacchetti più piccolo, mentre le piattaforme precedenti continueranno a funzionare ed eseguiranno il fallback alla destinazione netstandard1.x.

❌ NON includere una destinazione .NET Standard se la libreria si basa su un modello di app specifico della piattaforma.

Una libreria di toolkit di controlli della piattaforma UWP, ad esempio, dipende da un modello di app che è disponibile solo nella piattaforma UWP. Le API specifiche del modello di app non sono disponibili in .NET Standard.

Multitargeting

In alcuni casi è necessario accedere alle API specifiche del framework dalle librerie. Il modo migliore per chiamare le API specifiche del framework consiste nell'usare il multitargeting, che compila il progetto per numerosi framework di destinazione .NET invece che per uno solo.

Per evitare ai consumer di dover eseguire la compilazione per i singoli framework, è consigliabile cercare di avere un output .NET Standard e uno o più output specifici del framework. Con il multitargeting tutti gli assembly vengono inseriti all'interno di un singolo pacchetto NuGet. I consumer possono quindi fare riferimento allo stesso pacchetto NuGet e scegliere l'implementazione appropriata. La libreria .NET Standard serve come libreria di fallback usata in tutti i casi ad eccezione di quelli in cui il pacchetto NuGet offre un'implementazione specifica del framework. Il multitargeting consente di usare la compilazione condizionale nel codice e chiamare API specifiche del framework.

NuGet package with multiple assemblies

✔️ Valutare la scelta delle implementazioni .NET come destinazione in aggiunta a .NET Standard.

La scelta delle implementazioni .NET come destinazione consente di chiamare API specifiche della piattaforma al di fuori di .NET Standard.

Quando si esegue questa operazione, non eliminare il supporto per .NET Standard. Eseguire invece la generazione dall'implementazione e offrire API per le funzionalità. In questo modo, la libreria può essere usata ovunque e supporta l'abilitazione delle funzionalità in fase di esecuzione.

public static class GpsLocation
{
    // This project uses multi-targeting to expose device-specific APIs to .NET Standard.
    public static async Task<(double latitude, double longitude)> GetCoordinatesAsync()
    {
#if NET462
        return CallDotNetFrameworkApi();
#elif WINDOWS_UWP
        return CallUwpApi();
#else
        throw new PlatformNotSupportedException();
#endif
    }

    // Allows callers to check without having to catch PlatformNotSupportedException
    // or replicating the OS check.
    public static bool IsSupported
    {
        get
        {
#if NET462 || WINDOWS_UWP
            return true;
#else
            return false;
#endif
        }
    }
}

✔️ VALUTARE l'uso del multitargeting anche se il codice sorgente è lo stesso per tutte le destinazioni, quando il progetto include dipendenze di libreria o pacchetto.

I pacchetti dipendenti del progetto, diretti o downstream, potrebbero usare le stesse API di codice se ne viene eseguito il wrapping in versioni diverse dell'assembly dipendente per ogni framework di destinazione. L'aggiunta di destinazioni specifiche garantisce che i consumer non debbano aggiungere o aggiornare i reindirizzamenti delle associazioni di assembly.

❌ EVITARE il multitargeting e l'impostazione di .NET Standard come destinazione, se il codice sorgente è lo stesso per tutte le destinazioni e il progetto non ha dipendenze di libreria o pacchetto.

L'assembly .NET Standard verrà usato automaticamente da NuGet. La scelta come destinazione di singole implementazioni .NET comporta un aumento delle dimensioni di *.nupkg senza offrire alcun vantaggio.

✔️ VALUTARE l'aggiunta di una destinazione per net462 quando si offre una destinazione netstandard2.0.

L'uso di .NET Standard 2.0 da .NET Framework presenta alcuni problemi che sono stati risolti in .NET Framework 4.7.2. È possibile migliorare l'esperienza per gli sviluppatori che usano ancora .NET Framework 4.6.1-4.7.2 offrendo loro un file binario compilato per .NET Framework 4.6.2.

✔️ DA FARE Distribuire la libreria usando un pacchetto NuGet.

NuGet selezionerà la destinazione migliore per lo sviluppatore evitandogli di dover scegliere l'implementazione appropriata.

✔️ USARE la proprietà TargetFrameworks di un file di progetto quando si usa il multitargeting.

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <!-- This project will output netstandard2.0 and net462 assemblies -->
    <TargetFrameworks>netstandard2.0;net462</TargetFrameworks>
  </PropertyGroup>
</Project>

✔️ VALUTARE l'uso di MSBuild.Sdk.Extras con il multitargeting per la piattaforma UWP e Xamarin in quanto semplifica notevolmente il file di progetto.

❌ EVITARE di modificare il nome dell'assembly o di usare nomi di assembly diversi per ogni moniker framework di destinazione (TFM) compilato dalla libreria. A causa delle dipendenze tra librerie, il multitargeting con nomi di assembly diversi per TFM può causare interruzioni nei consumer di pacchetti. Un assembly deve avere lo stesso nome in tutti i TFM.

Destinazioni precedenti

.NET consente di scegliere come destinazione versioni di .NET Framework per le quali non è più disponibile il supporto e piattaforme non più usate comunemente. In questo modo, la libreria funzionerà con il maggior numero di destinazioni possibili, tuttavia sarà necessario lavoro aggiuntivo per trovare una soluzione alternativa per le API mancanti. Considerando la copertura e le limitazioni, non vale più la pena scegliere come destinazione determinati framework.

❌ NON includere come destinazione una libreria di classi portabile. Ad esempio: portable-net45+win8+wpa81+wp8.

.NET standard è il modo moderno per supportare le librerie .NET multipiattaforma e sostituisce le librerie di classi portabili.

❌ NON includere destinazioni per piattaforme .NET che non sono più supportate. Ad esempio, SL4, WP.