Apa implikasi aplikasi dari pustaka standar bersih bergantung pada metapackage?

100

Misalkan saya memiliki perpustakaan kelas yang saya ingin menargetkan netstandard1.3, tetapi juga menggunakan BigInteger. Berikut adalah contoh sepele - satu-satunya file sumber adalah 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);            
    }
}

Kembali ke dunia project.json, saya akan menargetkan netstandard1.3di frameworksbagian, dan memiliki ketergantungan eksplisit pada System.Runtime.Numerics, misalnya versi 4.0.1. Paket nuget yang saya buat hanya akan mencantumkan ketergantungan itu.

Dalam dunia baru yang berani berbasis csproj dotnet perkakas (Saya menggunakan v1.0.1 alat baris perintah) ada sebuah implisit paket metapackage referensi untuk NETStandard.Library 1.6.1saat menargetkan netstandard1.3. Ini berarti file proyek saya sangat kecil, karena tidak memerlukan ketergantungan eksplisit:

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

... tetapi paket nuget yang dihasilkan memiliki ketergantungan pada NETStandard.Library, yang menunjukkan bahwa untuk menggunakan perpustakaan kecil saya, Anda memerlukan semua yang ada di sana.

Ternyata saya bisa menonaktifkan fungsionalitas itu menggunakan DisableImplicitFrameworkReferences, lalu menambahkan dependensi secara manual lagi:

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

Sekarang paket NuGet saya menjelaskan dengan tepat apa itu tergantung. Secara intuitif, ini terasa seperti paket yang "lebih ramping".

Jadi, apa perbedaan yang tepat bagi konsumen perpustakaan saya? Jika seseorang mencoba menggunakannya dalam aplikasi UWP, apakah bentuk dependensi kedua yang "dipangkas" berarti aplikasi yang dihasilkan akan lebih kecil?

Dengan tidak mendokumentasikan DisableImplicitFrameworkReferencessecara jelas (sejauh yang saya lihat; saya membacanya dalam sebuah masalah ) dan dengan menjadikan ketergantungan implisit sebagai default saat membuat proyek, Microsoft mendorong pengguna untuk hanya bergantung pada metapackage - tetapi bagaimana saya bisa yakin itu tidak memiliki kerugian saat saya memproduksi paket perpustakaan kelas?

Jon Skeet
sumber
3
Perlu dicatat bahwa pemangkasan ketergantungan sedang dikerjakan . Saat ini, ukuran Hello World!aplikasi mandiri dikurangi menjadi <10MB.
ABarney
@ABarney 10MB masih terlalu banyak dibandingkan dengan ukuran < 20KB dari proyek klasik .NET Framework C # hello-world.
Dai

Jawaban:

76

Di masa lalu, kami telah memberikan rekomendasi kepada pengembang untuk tidak mereferensikan paket meta ( NETStandard.Library) dari paket NuGet tetapi mereferensikan paket individual, seperti System.Runtimedan System.Collections. Alasannya adalah bahwa kami menganggap paket meta sebagai singkatan dari sekumpulan paket yang merupakan blok penyusun atom sebenarnya dari platform .NET. Asumsinya adalah: kita mungkin akan membuat platform .NET lain yang hanya mendukung beberapa blok atom ini tetapi tidak semuanya. Karenanya, semakin sedikit paket yang Anda rujuk, semakin portabel Anda. Ada juga kekhawatiran tentang bagaimana perkakas kami menangani grafik paket besar.

Ke depannya, kami akan menyederhanakan ini:

  1. .NET Standard adalah blok bangunan atom . Dengan kata lain, platform baru tidak diizinkan untuk membuat subset .NET Standard - mereka harus menerapkan semuanya.

  2. Kami beralih dari menggunakan paket untuk mendeskripsikan platform kami , termasuk .NET Standard.

Artinya, Anda tidak perlu lagi merujuk paket NuGet apa pun untuk .NET Standard. Anda menyatakan ketergantungan Anda dengan folder lib, yang persis seperti cara kerjanya untuk semua platform .NET lainnya, khususnya .NET Framework.

Namun, saat ini perkakas kami masih akan terbakar dalam referensi ke NETStandard.Library. Tidak ada salahnya juga, itu hanya akan menjadi mubazir untuk bergerak maju.

Saya akan memperbarui FAQ di repo Standar .NET untuk menyertakan pertanyaan ini.

Pembaruan : Pertanyaan ini sekarang menjadi bagian dari FAQ .

Immo Landwerth
sumber
9
Terima kasih - ini sangat masuk akal. Saya pikir saya sebagian bingung karena di dunia project.json saya harus menambahkan dependensi bahkan ketika paket secara logis merupakan bagian dari kerangka kerja yang saya targetkan (misalnya System.Runtime.Numerics untuk netstandard1.3) - jadi saya pikir NETStandard.Library adalah benar-benar menariknya sebagai dependensi.
Jon Skeet
2
Kekecewaan. Saya menyukai gagasan mereferensikan paket, karena rasanya saya hanya dapat mereferensikan apa yang saya inginkan daripada seluruh "wastafel dapur". Mungkin saya tidak memikirkannya dengan benar?
Nate Barbettini
3
Anda tidak perlu memikirkannya dengan cara yang salah, tetapi pertanyaannya adalah mengapa Anda peduli. Penting untuk dipahami bahwa platform tidak dapat disusun tanpa batas. Ketika Anda berjalan di lingkungan kerangka bersama (.NET Framework, Mono, .NET Core) semua bit ini tetap ada. Jika Anda membangun aplikasi mandiri (Xamarin, Mono / .NET Core dengan linker), kami dapat secara otomatis memangkas berdasarkan penggunaan Anda yang sebenarnya. Jadi bagaimanapun juga, Anda tidak akan kehilangan apapun dengan tidak mereferensikan blok yang lebih kecil.
Immo Landwerth
3
Bagaimana dengan meminta kompilator menegakkan aturan seperti mencegah pengembang membuat permintaan http dalam proyek logika bisnis dengan tidak mengizinkan paket itu - membuang-buang tenaga?
David Ferretti
Belum tentu, tapi bagaimana Anda menegakkannya, yaitu apa yang menghentikan pengembang untuk menambahkan referensi? Saya akan menulis penganalisis yang disuntikkan pada waktu pembuatan untuk menerapkan aturan lapisan dan menyebabkan error pada mesin build.
Immo Landwerth
19

Tim biasanya merekomendasikan untuk mencari tahu apa set paket tertipis itu. Mereka tidak lagi melakukan ini, dan merekomendasikan orang-orang hanya membawa NETStandard.Library sebagai gantinya (dalam kasus proyek bergaya SDK, ini akan dilakukan secara otomatis untuk Anda).

Saya tidak pernah mendapatkan jawaban yang benar-benar terus terang tentang mengapa itu terjadi, jadi izinkan saya untuk membuat beberapa tebakan.

Alasan utamanya mungkin karena hal itu memungkinkan mereka menyembunyikan perbedaan versi pustaka dependen yang seharusnya Anda lacak sendiri saat mengubah kerangka kerja target. Ini juga merupakan sistem yang jauh lebih ramah pengguna dengan file proyek berbasis SDK, karena Anda terus terang tidak memerlukan referensi apa pun untuk mendapatkan bagian yang layak dari platform (seperti yang biasa Anda lakukan dengan referensi default di Desktop-land, terutama mscorlib ).

Dengan mendorong meta-definisi tentang apa artinya menjadi netstandardpustaka, atau netcoreappaplikasi ke dalam paket NuGet yang sesuai, mereka tidak perlu membangun pengetahuan khusus apa pun ke dalam definisi hal-hal itu seperti yang dilihat oleh Visual Studio (atau dotnet new).

Analisis statis dapat digunakan selama publikasi untuk membatasi DLL yang dikirim, yang merupakan sesuatu yang mereka lakukan saat ini saat melakukan kompilasi asli untuk UWP (meskipun dengan beberapa peringatan). Mereka tidak melakukannya hari ini untuk .NET Core, tetapi saya menganggap ini adalah pengoptimalan yang telah mereka pertimbangkan (serta mendukung kode asli).

Tidak ada yang menghentikan Anda untuk menjadi sangat selektif, jika Anda memilihnya. Saya yakin Anda akan menemukan bahwa Anda hampir menjadi satu-satunya yang melakukannya, yang juga mengalahkan tujuan (karena akan diasumsikan semua orang membawa NETStandard.Libraryatau Microsoft.NETCore.App).

Brad Wilson
sumber
6
Saya setuju itu pasti mengalahkan tujuan begitu ada lebih dari satu ketergantungan, jika ada yang membawa masuk NETStandard.Library. Ini agak terpenuhi dengan sendirinya, tentu saja ... jika saya bergantung pada NETStandard.LibraryWaktu Noda, itu berarti perpustakaan lain yang dibangun di atas Waktu Noda tidak memiliki alasan untuk memangkas ketergantungan, dll. Sangat menggoda untuk selektif untuk saat ini (Waktu Noda adalah menuju 2.0) kemudian bersantai sedikit kemudian setelah konvensi telah ditetapkan - mengubah dari selektif ke berbasis lib akan menjadi perubahan yang tidak melanggar, saya berasumsi, tetapi sebaliknya tidak benar.
Jon Skeet
8

Anda tidak perlu menonaktifkan referensi implisit. Semua platform tempat library dapat dijalankan sudah memiliki rakitan yang diperlukan NETStandard.Librarydependensi.

Pustaka Standar .NET adalah spesifikasi, kumpulan rakitan referensi yang Anda kompilasi yang menyediakan sekumpulan API yang dijamin ada di kumpulan platform dan versi platform yang diketahui, seperti .NET Core atau .NET Framework . Ini bukan implementasi dari rakitan ini, hanya bentuk API yang cukup untuk memungkinkan compiler berhasil membuat kode Anda.

Implementasi untuk API ini disediakan oleh platform target, seperti .NET Core, Mono, atau .NET Framework. Mereka dikirimkan dengan platform, karena mereka adalah bagian penting dari platform. Jadi tidak perlu menentukan set dependensi yang lebih kecil - semuanya sudah ada, Anda tidak akan mengubahnya.

The NETStandard.Librarypaket menyediakan majelis referensi tersebut. Satu hal yang membingungkan adalah nomor versi - paketnya adalah versi 1.6.1, tetapi ini tidak berarti ".NET Standard 1.6". Ini hanya versi paketnya.

Versi .NET Standard yang Anda targetkan berasal dari kerangka kerja target yang Anda tentukan dalam proyek Anda.

Jika Anda membuat pustaka dan ingin menjalankannya di .NET Standard 1.3, Anda akan mereferensikan NETStandard.Librarypaketnya, saat ini di versi 1.6.1. Tetapi yang lebih penting, file proyek Anda akan ditargetkan netstandard1.3.

The NETStandard.Librarypaket akan memberikan Anda satu set yang berbeda dari majelis referensi tergantung pada target kerangka moniker Anda (saya menyederhanakan untuk singkatnya, tetapi berpikir lib\netstandard1.0, lib\netstandard1.1dan kelompok ketergantungan ). Jadi jika proyek Anda menargetkan netstandard1.3, Anda akan mendapatkan rakitan referensi 1,3. Jika Anda menargetkan netstandard1.6, Anda akan mendapatkan rakitan referensi 1.6.

Jika Anda membuat aplikasi, Anda tidak dapat menargetkan .NET Standard. Ini tidak masuk akal - Anda tidak dapat menjalankan spesifikasi. Sebaliknya, Anda menargetkan platform beton, seperti net452atau netcoreapp1.1. NuGet mengetahui pemetaan antara platform ini dan netstandardmoniker kerangka target, jadi ketahui lib\netstandardX.Xfolder mana yang kompatibel dengan platform target Anda. Ia juga tahu bahwa dependensi NETStandard.Librarydipenuhi oleh platform target, jadi tidak akan menarik rakitan lain.

Demikian pula, saat membuat aplikasi .NET Core mandiri, rakitan implementasi .NET Standard disalin dengan aplikasi Anda. Referensi ke NETStandard.Librarytidak membawa aplikasi baru lainnya.

Perhatikan bahwa itu dotnet publishakan membuat aplikasi mandiri , tetapi saat ini tidak akan melakukan pemangkasan, dan akan menerbitkan semua rakitan. Ini akan ditangani secara otomatis oleh perkakas , jadi sekali lagi, memangkas dependensi di perpustakaan Anda tidak akan membantu di sini.

Satu-satunya tempat yang dapat saya bayangkan di mana mungkin membantu untuk menghapus NETStandard.Libraryreferensi adalah jika Anda menargetkan platform yang tidak mendukung .NET Standard, dan Anda dapat menemukan paket dari .NET Standard di mana semua dependensi transitif dapat berjalan di platform target Anda. Saya menduga tidak banyak paket yang sesuai dengan tagihan itu.

warga negara
sumber
Jika Anda melakukannya dotnet publishpada runtime tertentu, itu akan membawa semua dependensi, termasuk dotnet.exe (atau yang setara dengan Linux / OS X). Itu harus menjadi penerapan yang sepenuhnya berdiri sendiri pada saat itu. Lihat hasil untuk proyek uji unit: gist.github.com/bradwilson/6cc5a8fdfa18230aa6c99b851fb85c01
Brad Wilson
4
Saya pikir paragraf terakhir adalah masalahnya ... dan jika itu bukan masalah, mengapa kita membutuhkan versi netstandard1.x yang berbeda sama sekali? Jika setiap platform memiliki segalanya di netstandard1.6, mengapa bahkan memiliki netstandard1.0 sebagai konsep? Itu bagian yang membingungkan bagi saya.
Jon Skeet
1
Dan daftar platform yang mendukung .NET Standard tidak menyertakan SALAH SATU dari banyak platform Unity.
citizenmatt
1
(Kesabaran Anda sangat dihargai, btw. Saya sangat berharap ini semua masuk akal, dan ini akan membantu banyak, banyak pengembang ...)
Jon Skeet
1
Mari kita lanjutkan diskusi ini dalam obrolan .
citizenmatt