Jakie są konsekwencje zastosowania biblioteki netstandard w zależności od metapakietu?

100

Załóżmy, że mam bibliotekę klas, którą chcę kierować do netstandard1.3, ale także używam BigInteger. Oto trywialny przykład - jedyny plik źródłowy to 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);            
    }
}

Wracając do świata project.json, celowałbym netstandard1.3w tę frameworkssekcję i mam wyraźną zależność System.Runtime.Numericsnp. Od wersji 4.0.1. Utworzony przeze mnie pakiet nuget będzie zawierał tylko tę zależność.

W nowym, odważnym świecie narzędzi dotnet opartych na csproj (używam wersji 1.0.1 narzędzi wiersza polecenia) istnieje niejawne odwołanie do pakietu metapakietuNETStandard.Library 1.6.1 podczas określania celu netstandard1.3. Oznacza to, że mój plik projektu jest naprawdę mały, ponieważ nie wymaga wyraźnej zależności:

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

... ale utworzony pakiet nuget ma zależność od NETStandard.Library, co sugeruje, że aby korzystać z mojej małej biblioteki, potrzebujesz wszystkiego .

Okazuje się, że mogę wyłączyć tę funkcję za pomocą DisableImplicitFrameworkReferences, a następnie ręcznie dodać zależność:

<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>

Teraz mój pakiet NuGet mówi dokładnie, od czego zależy. Intuicyjnie wydaje się, że jest to „odchudzony” pakiet.

Jaka jest więc dokładna różnica dla konsumenta mojej biblioteki? Jeśli ktoś spróbuje go użyć w aplikacji UWP, czy druga, „przycięta” forma zależności oznacza, że ​​wynikowa aplikacja będzie mniejsza?

Nie dokumentując DisableImplicitFrameworkReferencesjasno (o ile widziałem; przeczytałem o tym w numerze ) i ustawiając niejawną zależność jako domyślną podczas tworzenia projektu, Microsoft zachęca użytkowników do polegania tylko na metapakiecie - ale jak mogę to zrobić na pewno to nie ma wad, kiedy tworzę pakiet biblioteki klas?

Jon Skeet
źródło
3
Należy zauważyć, że trwają prace nad przycinaniem zależności . Obecnie rozmiar Hello World!samodzielnej aplikacji jest zmniejszony do <10 MB.
ABarney
@ABarney 10 MB to wciąż o wiele za dużo w porównaniu do rozmiaru <20 KB klasycznego projektu hello-world .NET Framework C #.
Dai

Odpowiedzi:

76

W przeszłości NETStandard.Librarydawaliśmy deweloperom zalecenie, aby nie odwoływać się do metapakietu ( ) z pakietów NuGet, ale zamiast tego odwoływać się do poszczególnych pakietów, takich jak System.RuntimeiSystem.Collections . Powodem było to, że myśleliśmy o metapakiecie jako o skrócie dla grupy pakietów, które były rzeczywistymi atomowymi blokami budulcowymi platformy .NET. Założenie było takie: możemy w końcu stworzyć kolejną platformę .NET, która obsługuje tylko niektóre z tych atomowych bloków, ale nie wszystkie. Dlatego im mniej pakietów odwołujesz, tym bardziej będziesz przenośny. Pojawiły się również obawy dotyczące tego, jak nasze narzędzia radzą sobie z wykresami dużych pakietów.

Idąc dalej, uprościmy to:

  1. .NET Standard to atomowy blok konstrukcyjny . Innymi słowy, nowe platformy nie mogą mieć podzbioru .NET Standard - muszą to wszystko zaimplementować.

  2. Odchodzimy od używania pakietów do opisywania naszych platform , w tym .NET Standard.

Oznacza to, że nie będziesz już musiał odwoływać się do żadnych pakietów NuGet dla platformy .NET Standard. Wyraziłeś swoją zależność od folderu lib, dokładnie tak, jak działał na wszystkich innych platformach .NET, w szczególności .NET Framework.

Jednak w tej chwili nasze narzędzia nadal będą się palić w odniesieniu do NETStandard.Library. Nie ma w tym nic złego, posuwanie się naprzód po prostu stanie się zbędne.

Zaktualizuję często zadawane pytania dotyczące repozytorium .NET Standard, aby uwzględnić to pytanie.

Aktualizacja : to pytanie jest teraz częścią FAQ .

Immo Landwerth
źródło
9
Dzięki - to ma sens. Myślę, że byłem częściowo zdezorientowany, ponieważ w świecie project.json musiałem dodawać zależności, nawet gdy pakiety były logiczną częścią frameworka, na który celowałem (np. System.Runtime.Numerics dla netstandard1.3) - więc pomyślałem, że NETStandard.Library jest naprawdę wciągając je jako zależności.
Jon Skeet
2
Porażka. Podobał mi się pomysł odwoływania się do pakietów, ponieważ wydawało mi się, że mogę odnosić się tylko do tego, co chcę, zamiast do całego „zlewu kuchennego”. Może nie myślę o tym poprawnie?
Nate Barbettini
3
Niekoniecznie myślisz o tym w niewłaściwy sposób, ale pytanie brzmi, dlaczego ci zależy. Ważne jest, aby zrozumieć, że platforma nie jest nieskończenie konfigurowalna. W przypadku uruchamiania we współdzielonym środowisku frameworkowym (.NET Framework, Mono, .NET Core) wszystkie te bity są obecne. Jeśli tworzysz samodzielną aplikację (Xamarin, Mono / .NET Core z konsolidatorem), możemy automatycznie przycinać na podstawie Twojego rzeczywistego użycia. Tak czy inaczej, niczego nie tracisz, nie odwołując się do mniejszych bloków.
Immo Landwerth
3
A co z tym, że kompilator wymusza reguły, takie jak zapobieganie wysyłaniu przez programistę żądania HTTP w projekcie logiki biznesowej, nie zezwalając na ten pakiet - strata wysiłku?
David Ferretti
Niekoniecznie, ale jak to egzekwować, czyli co powstrzymuje programistę przed dodaniem referencji? Napisałbym analizator, który jest wstrzykiwany w czasie kompilacji, aby wymusić reguły warstwy i powoduje błędy na maszynie kompilacji.
Immo Landwerth
19

Zespół zalecał ustalenie, jaki jest najcieńszy zestaw opakowań. Nie robią już tego i zalecają ludziom po prostu przyniesienie NETStandard.Library (w przypadku projektu w stylu SDK zostanie to zrobione automatycznie).

Nigdy nie uzyskałem całkowicie prostej odpowiedzi na pytanie, dlaczego tak się dzieje, więc pozwól mi zrobić kilka świadomych domysłów.

Głównym powodem jest prawdopodobnie to, że pozwala im to ukryć różnice w wersjach bibliotek zależnych, które w innym przypadku musielibyście śledzić podczas zmiany platform docelowych. Jest to również znacznie bardziej przyjazny dla użytkownika system z plikami projektu opartymi na SDK, ponieważ szczerze mówiąc nie potrzebujesz żadnych odniesień, aby uzyskać przyzwoitą część platformy (tak jak kiedyś z domyślnymi referencjami w Desktop-land, zwłaszcza mscorlib ).

Wypychając meta-definicję tego, co to znaczy być netstandardbiblioteką lub netcoreappaplikacją do odpowiedniego pakietu NuGet, nie muszą oni budować żadnej specjalnej wiedzy w definicji tych rzeczy, gdy program Visual Studio (lub dotnet new) je widzi.

Analiza statyczna może zostać użyta podczas publikowania, aby ograniczyć dostarczane biblioteki DLL, co jest czymś, co robią dzisiaj, wykonując natywną kompilację dla platformy UWP (aczkolwiek z pewnymi zastrzeżeniami). Nie robią tego dzisiaj dla .NET Core, ale przypuszczam, że jest to optymalizacja, którą rozważali (a także obsługa kodu natywnego).

Nic nie stoi na przeszkodzie, abyś był bardzo selektywny, jeśli tak zdecydujesz. Wierzę, że przekonasz się, że jesteś prawie jedyną osobą, która to robi, co również mija się z celem (ponieważ zakłada się, że wszyscy wprowadzają NETStandard.Librarylub Microsoft.NETCore.App).

Brad Wilson
źródło
6
Zgadzam się, że na pewno jest to sprzeczne z celem, gdy istnieje więcej niż jedna zależność, jeśli któraś z nich wnosi NETStandard.Library. To nieco samospełniające, oczywiście ... jeśli mi zależeć NETStandard.Libraryna Noda Time, to znaczy każda inna biblioteka zbudowany na szczycie Noda Czasu nie ma powodu do wykończenia zależności itd aż korci, żeby być selektywny teraz (Noda czas zmierzając w kierunku 2.0), a potem nieco później, gdy już zostaną ustalone konwencje, rozluźnij się - zmiana z selektywnej na opartą na bibliotekach byłaby, jak zakładam, niezłomną zmianą, ale sytuacja odwrotna nie jest prawdą.
Jon Skeet
8

Nie powinno być potrzeby wyłączania niejawnego odwołania. Wszystkie platformy, na których będzie można uruchomić bibliotekę, będą już miały zestawy, których NETStandard.Librarywymagałaby zależność.

Biblioteka .NET Standard Library to specyfikacja, zestaw zestawów referencyjnych, na podstawie których można kompilować, który zapewnia zestaw interfejsów API, które z pewnością istnieją na znanym zestawie platform i wersjach platform, takich jak .NET Core lub .NET Framework . Nie jest to implementacja tych zestawów, wystarczy kształt interfejsu API, aby umożliwić kompilatorowi pomyślne zbudowanie kodu.

Implementacja tych interfejsów API jest zapewniana przez platformę docelową, taką jak .NET Core, Mono lub .NET Framework. Są dostarczane z platformą, ponieważ są istotną częścią platformy. Nie ma więc potrzeby określania mniejszego zestawu zależności - wszystko już tam jest, nie zmienisz tego.

NETStandard.LibraryPakiet zawiera te zespoły referencyjne. Nieporozumieniem jest numer wersji - pakiet to wersja 1.6.1, ale nie oznacza to „.NET Standard 1.6”. To tylko wersja pakietu.

Wersja platformy .NET Standard, na którą kierujesz reklamy, pochodzi z platformy docelowej określonej w projekcie.

Jeśli tworzysz bibliotekę i chcesz, aby działała w .NET Standard 1.3, powinieneś odwołać się do NETStandard.Librarypakietu, obecnie w wersji 1.6.1. Ale co ważniejsze, plik projektu byłby przeznaczony dla netstandard1.3.

NETStandard.LibraryPakiet daje innego zestawu zespołów referencyjnych w zależności od swojej docelowej ramowej pseudonimem (jestem uproszczenie dla zwięzłości, ale myślę lib\netstandard1.0, lib\netstandard1.1a grupy z zależnościami ). Więc jeśli Twój projekt jest przeznaczony dla netstandard1.3Ciebie, otrzymasz zestawy referencyjne 1.3. Jeśli celujesz netstandard1.6, otrzymasz zestawy referencyjne 1.6.

Jeśli tworzysz aplikację, nie możesz kierować na platformę .NET Standard. To nie ma sensu - nie można uruchomić na specyfikacji. Zamiast tego kierujesz reklamy na platformy betonowe, takie jak net452lub netcoreapp1.1. NuGet zna mapowanie między tymi platformami i monikerami platformy netstandarddocelowej, dzięki czemu wie, które lib\netstandardX.Xfoldery są zgodne z platformą docelową. Wie również, że zależności NETStandard.Librarysą spełnione przez platformę docelową, więc nie będzie pobierać żadnych innych zestawów.

Podobnie podczas tworzenia autonomicznej aplikacji .NET Core zestawy implementacji .NET Standard są kopiowane z aplikacją. Odniesienie do NETStandard.Librarynie wprowadza żadnych innych nowych aplikacji.

Zwróć uwagę, że dotnet publishutworzy samodzielną aplikację , ale obecnie nie będzie ona przycinać i opublikuje wszystkie zestawy. Będzie to obsługiwane automatycznie przez narzędzia , więc ponownie, przycinanie zależności w twojej bibliotece nie pomoże tutaj.

Jedyne miejsce, w którym mogę sobie wyobrazić, w którym może pomóc usunąć NETStandard.Libraryodwołanie, to sytuacja, w której celujesz w platformę, która nie obsługuje standardu .NET, i możesz znaleźć pakiet ze standardu .NET, w którym mogą działać wszystkie zależności przechodnie na platformie docelowej. Podejrzewam, że nie ma wielu paczek, które pasowałyby do tego rachunku.

citizenmatt
źródło
Jeśli zrobisz dotnet publishto w określonym środowisku uruchomieniowym, przyniesie ono wszystkie zależności, w tym dotnet.exe (lub jego odpowiednik w systemie Linux / OS X). W tym momencie powinno to być całkowicie samodzielne wdrożenie. Sprawdź wyniki projektu testu jednostkowego: gist.github.com/bradwilson/6cc5a8fdfa18230aa6c99b851fb85c01
Brad Wilson
4
Myślę, że ostatni akapit jest dokładnie tym problemem ... a gdyby to nie był problem, po co mielibyśmy w ogóle potrzebować różnych wersji netstandard1.x? Jeśli każda platforma ma wszystko w netstandard1.6, po co w ogóle mieć koncepcję netstandard1.0? To jest dla mnie myląca część.
Jon Skeet
1
A lista platform obsługujących .NET Standard nie obejmuje ŻADNEJ z wielu platform Unity.
citizenmatt
1
(Twoja cierpliwość jest bardzo ceniona, btw mam naprawdę nadzieję To wszystko ma sens, i że będzie to pomocne dla wielu, wielu programistów ....)
Jon Skeet
1
Daj nam kontynuować tę dyskusję w czacie .
citizenmatt