Quali sono le implicazioni applicative di una libreria netstandard a seconda di un metapacchetto?


100

Supponiamo che io abbia una libreria di classi che voglio indirizzare a netstandard1.3, ma che usi anche BigInteger. Ecco un esempio banale: l'unico file sorgente è Adder.cs:

using System;
using System.Numerics;

namespace Calculator
{
    public class Adder
    {
        public static BigInteger Add(int x, int y)
            => new BigInteger(x) + new BigInteger(y);            
    }
}

Tornando al mondo di project.json, sceglierei come target netstandard1.3nella frameworkssezione e dipenderei esplicitamente System.Runtime.Numerics, ad esempio dalla versione 4.0.1. Il pacchetto nuget che creo elencherà solo quella dipendenza.

Nel nuovo e coraggioso mondo degli strumenti dotnet basati su csproj (sto usando la v1.0.1 degli strumenti della riga di comando) c'è un riferimento implicito al pacchetto metapacchetto a NETStandard.Library 1.6.1cui mirare netstandard1.3. Ciò significa che il mio file di progetto è davvero piccolo, perché non necessita della dipendenza esplicita:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>netstandard1.3</TargetFramework>
  </PropertyGroup>
</Project>

... ma il pacchetto nuget prodotto ha una dipendenza da NETStandard.Library, il che suggerisce che per usare la mia piccola libreria, hai bisogno di tutto lì.

Risulta che posso disabilitare quella funzionalità usando DisableImplicitFrameworkReferences, quindi aggiungere di nuovo manualmente la dipendenza:

<Project Sdk="Microsoft.NET.Sdk">    
  <PropertyGroup>
    <TargetFramework>netstandard1.3</TargetFramework>
    <DisableImplicitFrameworkReferences>true</DisableImplicitFrameworkReferences>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="System.Runtime.Numerics" Version="4.0.1" />
  </ItemGroup>    
</Project>

Ora il mio pacchetto NuGet dice esattamente da cosa dipende. Intuitivamente, questo sembra un pacchetto "più snello".

Allora qual è la differenza esatta per un utente della mia libreria? Se qualcuno cerca di usarlo in un'applicazione UWP, la seconda forma di dipendenze "tagliata" significa che l'applicazione risultante sarà più piccola?

Non documentando DisableImplicitFrameworkReferenceschiaramente (per quanto ho visto; ne ho letto in un numero ) e rendendo la dipendenza implicita l'impostazione predefinita durante la creazione di un progetto, Microsoft incoraggia gli utenti a dipendere solo dal metapacchetto, ma come posso esserlo sicuro che non abbia svantaggi quando produco un pacchetto di libreria di classi?


3
Va notato che si sta lavorando sul trimming delle dipendenze . Attualmente, la dimensione di un'applicazione Hello World!autonoma è ridotta a <10 MB.
ABarney

@ABarney 10MB è ancora troppo rispetto alla dimensione < 20KB di un classico progetto .NET Framework C # hello-world.
Dai

Risposte:


76

In passato, abbiamo consigliato agli sviluppatori di non fare riferimento al metapacchetto ( NETStandard.Library) dai pacchetti NuGet, ma di fare invece riferimento a singoli pacchetti, come System.Runtimee System.Collections. La logica era che abbiamo pensato al metapacchetto come una scorciatoia per un gruppo di pacchetti che erano i veri e propri blocchi atomici della piattaforma .NET. Il presupposto era: potremmo finire per creare un'altra piattaforma .NET che supporta solo alcuni di questi blocchi atomici ma non tutti. Quindi, meno pacchetti fai riferimento, più portatile saresti. C'erano anche preoccupazioni riguardo al modo in cui la nostra strumentazione gestisce i grafici di grandi pacchetti.

Andando avanti, semplificheremo questo:

  1. .NET Standard è un blocco di costruzione atomico . In altre parole, le nuove piattaforme non sono autorizzate a subsetare .NET Standard: devono implementarlo tutto.

  2. Stiamo abbandonando l'utilizzo dei pacchetti per descrivere le nostre piattaforme , incluso .NET Standard.

Ciò significa che non sarà più necessario fare riferimento a nessun pacchetto NuGet per .NET Standard. Hai espresso la tua dipendenza con la cartella lib, che è esattamente come ha funzionato per tutte le altre piattaforme .NET, in particolare .NET Framework.

Tuttavia, in questo momento i nostri strumenti continueranno a bruciare nel riferimento a NETStandard.Library. Non c'è nulla di male nemmeno in questo, diventerà ridondante andando avanti.

Aggiornerò le domande frequenti sul repository .NET Standard per includere questa domanda.

Aggiornamento : questa domanda fa ora parte delle FAQ .


9
Grazie - questo ha molto senso. Penso di essere in parte confuso perché nel mondo project.json ho dovuto aggiungere dipendenze anche quando i pacchetti facevano parte logicamente del framework che stavo prendendo di mira (ad esempio System.Runtime.Numerics per netstandard1.3), quindi ho pensato che NETStandard.Library fosse trascinandoli davvero come dipendenze.
Jon Skeet

2
Bummer. Mi piaceva l'idea di fare riferimento ai pacchetti, perché mi sembrava di poter fare riferimento solo a ciò che volevo invece dell'intero "lavello della cucina". Forse non ci sto pensando correttamente?
Nate Barbettini

3
Non ci stai necessariamente pensando nel modo sbagliato, ma la domanda è perché ti interessa. È importante capire che la piattaforma non è componibile all'infinito. Quando si esegue in un ambiente framework condiviso (.NET Framework, Mono, .NET Core) tutti questi bit sono comunque presenti. Se crei un'app autonoma (Xamarin, Mono / .NET Core con linker) possiamo tagliare automaticamente in base al tuo utilizzo effettivo. Quindi, in ogni caso, non stai perdendo nulla non facendo riferimento a blocchi più piccoli.
Immo Landwerth

3
Che dire se il compilatore imponga regole come impedire a uno sviluppatore di effettuare una richiesta http in un progetto di logica aziendale non consentendo quel pacchetto - spreco di fatica?
David Ferretti

Non necessariamente, ma come lo imponi, ovvero cosa impedisce a uno sviluppatore di aggiungere il riferimento? Scriverei un analizzatore che viene iniettato in fase di compilazione per applicare le regole di livello e causare errori sulla macchina di compilazione.
Immo Landwerth

19

Il team era solito consigliare di capire quale fosse il pacchetto più sottile. Non lo fanno più e consigliano alle persone di portare invece NETStandard.Library (nel caso di un progetto in stile SDK, questo verrà fatto automaticamente per te).

Non ho mai avuto una risposta del tutto semplice sul perché fosse, quindi permettimi di fare alcune ipotesi plausibili.

È probabile che il motivo principale sia che consente loro di nascondere le differenze nelle versioni delle librerie dipendenti che altrimenti sarebbe necessario tenere traccia di se stessi quando si cambiano i framework di destinazione. È anche un sistema molto più intuitivo con i file di progetto basati su SDK, perché francamente non hai bisogno di riferimenti per ottenere una parte decente della piattaforma (proprio come facevi con i riferimenti predefiniti in Desktop-land, in particolare mscorlib ).

Inserendo la meta-definizione di cosa significa essere una netstandardlibreria o netcoreappun'applicazione nel pacchetto NuGet appropriato, non è necessario creare alcuna conoscenza speciale nella definizione di tali cose come le dotnet newvede Visual Studio (o ).

L'analisi statica potrebbe essere utilizzata durante la pubblicazione per limitare le DLL fornite, cosa che fanno oggi quando si esegue la compilazione nativa per UWP (anche se con alcuni avvertimenti). Oggi non lo fanno per .NET Core, ma presumo che sia un'ottimizzazione che hanno considerato (oltre a supportare il codice nativo).

Non c'è nulla che ti impedisca di essere molto selettivo, se lo desideri. Credo che scoprirai che sei quasi l'unico a farlo, il che vanifica anche lo scopo (poiché si presume che tutti stiano portando NETStandard.Libraryo Microsoft.NETCore.App).


6
Sono d'accordo che sconfigge definitivamente lo scopo una volta che c'è più di una dipendenza, se qualcuno di loro porta in sé NETStandard.Library. E 'un po' che si autoavvera, naturalmente ... se io dipenderà NETStandard.Libraryper Noda tempo, che significa qualsiasi altra libreria costruita sulla cima di Noda tempo non ha motivo di dipendenze di assetto, ecc E 'forte la tentazione di essere selettivi per ora (Noda tempo è dirigendosi verso 2.0) quindi rilassati un po 'più tardi una volta stabilite le convenzioni: il passaggio da selettivo a basato su lib sarebbe un cambiamento senza interruzioni, presumo, ma non è vero il contrario.
Jon Skeet

8

Non dovrebbe essere necessario disabilitare il riferimento implicito. Tutte le piattaforme su cui la libreria potrà essere eseguita avranno già gli assembly richiesti dalla NETStandard.Librarydipendenza.

La libreria .NET Standard è una specifica, un set di assembly di riferimento su cui eseguire la compilazione che fornisce un set di API di cui è garantita l'esistenza su un set noto di piattaforme e versioni di piattaforme, come .NET Core o .NET Framework . Non è un'implementazione di questi assembly, quanto basta della forma API per consentire al compilatore di creare correttamente il codice.

L'implementazione di queste API viene fornita da una piattaforma di destinazione, come .NET Core, Mono o .NET Framework. Spediscono con la piattaforma, perché sono una parte essenziale della piattaforma. Quindi non è necessario specificare un set di dipendenze più piccolo: tutto è già lì, non lo cambierai.

Il NETStandard.Librarypacchetto fornisce questi assembly di riferimento. Un punto di confusione è il numero di versione: il pacchetto è la versione 1.6.1, ma questo non significa ".NET Standard 1.6". È solo la versione del pacchetto.

La versione di .NET Standard a cui ti rivolgi proviene dal framework di destinazione specificato nel progetto.

Se stai creando una libreria e desideri che venga eseguita su .NET Standard 1.3, devi fare riferimento al NETStandard.Librarypacchetto, attualmente alla versione 1.6.1. Ma ancora più importante, il tuo file di progetto sarebbe target netstandard1.3.

Il NETStandard.Librarypacchetto vi darà un diverso insieme di assembly di riferimento a seconda del vostro target quadro moniker (sto semplificando per brevità, a meno di pensare lib\netstandard1.0, lib\netstandard1.1e gruppi di dipendenza ). Quindi, se il tuo progetto è destinato netstandard1.3, otterrai gli assembly di riferimento 1.3. Se scegli come target netstandard1.6, otterrai gli assembly di riferimento 1.6.

Se stai creando un'applicazione, non puoi scegliere come target .NET Standard. Non ha senso: non puoi eseguire una specifica. Invece, scegli come target piattaforme concrete, come net452o netcoreapp1.1. NuGet conosce il mapping tra queste piattaforme e i netstandardmoniker del framework di destinazione, quindi sa quali lib\netstandardX.Xcartelle sono compatibili con la piattaforma di destinazione. Sa anche che le dipendenze di NETStandard.Librarysono soddisfatte dalla piattaforma di destinazione, quindi non inserirà altri assembly.

Allo stesso modo, quando si crea un'app .NET Core autonoma, gli assembly di implementazione di .NET Standard vengono copiati con l'app. Il riferimento a NETStandard.Librarynon introduce altre nuove app.

Si noti che dotnet publishcreerà un'applicazione autonoma , ma attualmente non esegue il taglio e pubblicherà tutti gli assembly. Questo verrà gestito automaticamente dagli strumenti , quindi di nuovo, tagliare le dipendenze nella tua libreria non aiuterà qui.

L'unico posto in cui posso immaginare dove potrebbe essere utile rimuovere il NETStandard.Libraryriferimento è se stai prendendo di mira una piattaforma che non supporta .NET Standard e puoi trovare un pacchetto da .NET Standard in cui possono essere eseguite tutte le dipendenze transitive sulla tua piattaforma di destinazione. Sospetto che non ci siano molti pacchetti adatti a quel conto.


Se lo fai dotnet publishsu un runtime specifico, porterà tutte le dipendenze, incluso dotnet.exe (o il suo equivalente Linux / OS X). A quel punto dovrebbe essere una distribuzione completamente autonoma. Controlla i risultati di un progetto di unit test: gist.github.com/bradwilson/6cc5a8fdfa18230aa6c99b851fb85c01
Brad Wilson

4
Penso che l'ultimo paragrafo sia esattamente il problema ... e se non fosse un problema, perché avremmo bisogno di versioni diverse di netstandard1.x? Se ogni piattaforma ha tutto in netstandard1.6, perché anche avere netstandard1.0 come concetto? Questa è la parte che mi confonde.
Jon Skeet

1
E l'elenco delle piattaforme che supportano .NET Standard non include NESSUNA delle numerose piattaforme Unity.
citizenmatt

1
(La tua pazienza è molto apprezzata, btw. Spero davvero che tutto questo abbia un senso e che possa essere di aiuto a molti, molti sviluppatori ...)
Jon Skeet

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.