Những tác động ứng dụng của thư viện net tiêu chuẩn phụ thuộc vào một gói siêu nhỏ là gì?


100

Giả sử tôi có một thư viện lớp mà tôi muốn nhắm mục tiêu netstandard1.3, nhưng cũng sử dụng BigInteger. Đây là một ví dụ nhỏ - tệp nguồn duy nhất là 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);            
    }
}

Quay trở lại thế giới của project.json, tôi sẽ nhắm mục tiêu netstandard1.3trong frameworksphần và có phụ thuộc rõ ràng vào System.Runtime.Numerics, ví dụ: phiên bản 4.0.1. Gói nuget mà tôi tạo sẽ chỉ liệt kê sự phụ thuộc đó.

Trong thế giới mới dũng cảm của csproj dựa trên DotNet dụng cụ (Tôi đang sử dụng v1.0.1 trong những công cụ dòng lệnh) có một gói siêu gói tài liệu tham khảo ngầm để NETStandard.Library 1.6.1khi nhắm mục tiêu netstandard1.3. Điều này có nghĩa là tệp dự án của tôi thực sự nhỏ, vì nó không cần phụ thuộc rõ ràng:

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

... nhưng gói nuget được tạo ra có phụ thuộc vào NETStandard.Library, điều này gợi ý rằng để sử dụng thư viện nhỏ của tôi, bạn cần mọi thứ ở đó.

Hóa ra tôi có thể tắt chức năng đó bằng cách sử dụng DisableImplicitFrameworkReferences, sau đó thêm lại phần phụ thuộc theo cách thủ công:

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

Bây giờ gói NuGet của tôi nói chính xác những gì nó phụ thuộc vào. Trực quan, điều này có cảm giác như một gói "gọn gàng hơn".

Vậy sự khác biệt chính xác đối với người tiêu dùng thư viện của tôi là gì? Nếu ai đó cố gắng sử dụng nó trong một ứng dụng UWP, thì hình thức phụ thuộc thứ hai, được "cắt bớt" có nghĩa là ứng dụng kết quả sẽ nhỏ hơn không?

Bằng cách không ghi lại tài liệu DisableImplicitFrameworkReferencesrõ ràng (theo như tôi đã thấy; tôi đã đọc về nó trong một số báo ) và bằng cách đặt sự phụ thuộc ngầm định làm mặc định khi tạo một dự án, Microsoft đang khuyến khích người dùng chỉ phụ thuộc vào gói siêu nhỏ - nhưng làm sao tôi có thể chắc chắn rằng điều đó không có nhược điểm khi tôi tạo một gói thư viện lớp?


3
Cần lưu ý rằng việc cắt tỉa phụ thuộc đang được thực hiện . Hiện tại, kích thước của một Hello World!ứng dụng độc lập giảm xuống còn <10MB.
ABarney

@ABarney 10MB vẫn là quá nhiều so với kích thước < 20KB của một dự án .NET Framework C # hello-world cổ điển.
Đài

Câu trả lời:


76

Trước đây, chúng tôi đã đưa ra khuyến nghị cho các nhà phát triển không tham chiếu gói meta ( NETStandard.Library) từ các gói NuGet mà thay vào đó tham chiếu các gói riêng lẻ, như System.RuntimeSystem.Collections. Cơ sở lý luận là chúng tôi nghĩ về gói meta như một cách viết tắt của một loạt các gói là khối xây dựng nguyên tử thực tế của nền tảng .NET. Giả định là: cuối cùng chúng ta có thể tạo ra một nền tảng .NET khác chỉ hỗ trợ một số khối nguyên tử này chứ không phải tất cả chúng. Do đó, bạn tham khảo càng ít gói, bạn càng dễ di chuyển hơn. Cũng có những lo ngại về cách công cụ của chúng tôi xử lý các biểu đồ gói lớn.

Trong tương lai, chúng tôi sẽ đơn giản hóa điều này:

  1. .NET Standard là một khối xây dựng nguyên tử . Nói cách khác, các nền tảng mới không được phép thiết lập con .NET Standard - họ phải triển khai tất cả.

  2. Chúng tôi đang chuyển dần khỏi việc sử dụng các gói để mô tả các nền tảng của chúng tôi , bao gồm cả .NET Standard.

Điều này có nghĩa là, bạn sẽ không phải tham chiếu đến bất kỳ gói NuGet nào cho .NET Standard nữa. Bạn đã thể hiện sự phụ thuộc của mình với thư mục lib, đây chính xác là cách nó hoạt động cho tất cả các nền tảng .NET khác, đặc biệt là .NET Framework.

Tuy nhiên, ngay bây giờ công cụ của chúng tôi vẫn sẽ ghi trong tham chiếu tới NETStandard.Library. Điều đó cũng không có hại gì cả, nó sẽ trở nên thừa thãi khi tiến về phía trước.

Tôi sẽ cập nhật Câu hỏi thường gặp trên kho .NET Standard để bao gồm câu hỏi này.

Cập nhật : Câu hỏi này hiện là một phần của Câu hỏi thường gặp .


9
Cảm ơn - điều này rất có ý nghĩa. Tôi nghĩ rằng tôi đã bối rối một phần vì trong thế giới project.json, tôi phải thêm các phần phụ thuộc ngay cả khi các gói là một phần hợp lý của khung mà tôi đang nhắm mục tiêu (ví dụ: System.Runtime.Numerics cho netstandard1.3) - vì vậy tôi nghĩ NETStandard. thực sự kéo họ vào như là phụ thuộc.
Jon Skeet

2
Bummer. Tôi thích ý tưởng tham khảo các gói, vì có cảm giác như tôi chỉ có thể tham khảo những gì tôi muốn thay vì toàn bộ "bồn rửa nhà bếp". Có lẽ tôi không nghĩ về nó một cách chính xác?
Nate Barbettini

3
Bạn không hẳn đang nghĩ về nó một cách sai lầm, nhưng câu hỏi là tại sao bạn lại quan tâm. Điều quan trọng là phải hiểu rằng nền tảng này không thể kết hợp vô hạn. Khi bạn chạy trong môi trường khuôn khổ được chia sẻ (.NET Framework, Mono, .NET Core), tất cả các bit này vẫn có mặt. Nếu bạn xây dựng một ứng dụng độc lập (Xamarin, Mono / .NET Core với trình liên kết), chúng tôi có thể tự động cắt bớt dựa trên mức sử dụng thực tế của bạn. Vì vậy, dù bằng cách nào, bạn sẽ không mất gì khi không tham chiếu đến các khối nhỏ hơn.
Immo Landwerth

3
Điều gì về việc để trình biên dịch thực thi các quy tắc như ngăn nhà phát triển thực hiện yêu cầu http trong một dự án logic nghiệp vụ bằng cách không cho phép gói đó - lãng phí công sức?
David Ferretti

Không nhất thiết, nhưng làm thế nào để bạn thực thi nó, tức là điều gì ngăn một nhà phát triển thêm tham chiếu? Tôi sẽ viết một trình phân tích được đưa vào tại thời điểm xây dựng để thực thi các quy tắc lớp và gây ra lỗi trên máy xây dựng.
Immo Landwerth

19

Nhóm đã từng khuyên bạn nên tìm ra gói nào là gói mỏng nhất. Họ không còn làm điều này nữa và thay vào đó, mọi người chỉ cần sử dụng NETStandard.Library (trong trường hợp dự án kiểu SDK, điều này sẽ được thực hiện tự động cho bạn).

Tôi chưa bao giờ có câu trả lời hoàn toàn thẳng thắn về lý do tại sao lại như vậy, vì vậy hãy cho phép tôi đưa ra một số phỏng đoán có học thức.

Lý do chính có khả năng là nó cho phép họ ẩn sự khác biệt trong các phiên bản của các thư viện phụ thuộc mà nếu không, bạn sẽ được yêu cầu tự theo dõi khi thay đổi khung mục tiêu. Nó cũng là một hệ thống thân thiện với người dùng hơn nhiều với các tệp dự án dựa trên SDK, bởi vì bạn thực sự không cần bất kỳ tham chiếu nào để có được một phần nền tảng phù hợp (giống như bạn đã từng làm với các tham chiếu mặc định trong Desktop-land, đặc biệt là mscorlib ).

Bằng cách đẩy định nghĩa meta về ý nghĩa của netstandardthư viện hoặc netcoreappứng dụng vào gói NuGet thích hợp, họ không phải xây dựng bất kỳ kiến ​​thức đặc biệt nào vào định nghĩa của những thứ đó như Visual Studio (hoặc dotnet new) nhìn thấy chúng.

Phân tích tĩnh có thể được sử dụng trong quá trình xuất bản để hạn chế các tệp DLL được vận chuyển, đó là điều họ làm ngày nay khi thực hiện biên dịch gốc cho UWP (mặc dù có một số lưu ý). Họ không làm điều đó ngày hôm nay cho .NET Core, nhưng tôi cho rằng đó là một cách tối ưu hóa mà họ đã xem xét (cũng như hỗ trợ mã gốc).

Không có gì ngăn cản bạn rất chọn lọc, nếu bạn chọn. Tôi tin rằng bạn sẽ thấy rằng bạn gần như là người duy nhất làm điều đó, điều này cũng làm mất đi mục đích (vì có thể mọi người đều mang đến NETStandard.Libraryhoặc Microsoft.NETCore.App).


6
Tôi đồng ý rằng nó chắc chắn đánh bại mục đích một khi có nhiều hơn một sự phụ thuộc, nếu bất kỳ thứ nào trong số họ mang lại NETStandard.Library. Tất nhiên, nó có phần tự hoàn thiện ... nếu tôi phụ thuộc vào NETStandard.LibraryNoda Time, điều đó có nghĩa là bất kỳ thư viện nào khác được xây dựng trên Noda Time không có lý do gì để cắt bớt các phụ thuộc, v.v. Hiện tại tôi rất muốn chọn lọc (Noda Time là hướng tới 2.0) sau đó thư giãn một chút sau khi các quy ước đã được thiết lập - thay đổi từ chọn lọc sang dựa trên lib sẽ là một thay đổi không phá vỡ, tôi giả sử, nhưng điều ngược lại là không đúng.
Jon Skeet

8

Bạn không cần phải tắt tham chiếu ngầm định. Tất cả các nền tảng mà thư viện có thể chạy trên đó sẽ có sẵn các tập hợp mà phần NETStandard.Libraryphụ thuộc sẽ yêu cầu.

Thư viện tiêu chuẩn .NET là một đặc tả, một tập hợp các tham chiếu mà bạn biên dịch để cung cấp một tập hợp các API được đảm bảo tồn tại trên một tập hợp các nền tảng và phiên bản nền tảng, chẳng hạn như .NET Core hoặc .NET Framework . Nó không phải là một triển khai của các hội đồng này, chỉ đủ hình dạng API để cho phép trình biên dịch xây dựng thành công mã của bạn.

Việc triển khai các API này được cung cấp bởi một nền tảng đích, chẳng hạn như .NET Core, Mono hoặc .NET Framework. Họ vận chuyển cùng với nền tảng, bởi vì chúng là một phần thiết yếu của nền tảng. Vì vậy, không cần phải chỉ định một tập hợp phụ thuộc nhỏ hơn - mọi thứ đã ở đó, bạn sẽ không thay đổi điều đó.

Các NETStandard.Librarygói cung cấp các hội đồng tham khảo. Một điểm gây nhầm lẫn là số phiên bản - gói là phiên bản 1.6.1, nhưng điều này không có nghĩa là ".NET Standard 1.6". Nó chỉ là phiên bản của gói.

Phiên bản .NET Standard mà bạn đang nhắm mục tiêu đến từ khung mục tiêu mà bạn chỉ định trong dự án của mình.

Nếu bạn đang tạo thư viện và muốn nó chạy trên .NET Standard 1.3, bạn phải tham chiếu NETStandard.Librarygói, hiện đang ở phiên bản 1.6.1. Nhưng quan trọng hơn, tệp dự án của bạn sẽ nhắm mục tiêu netstandard1.3.

Các NETStandard.Librarygói phần mềm sẽ cung cấp cho bạn một bộ khác nhau của cụm tham khảo tùy thuộc vào mục tiêu khung biệt danh của bạn (tôi đơn giản hóa cho ngắn gọn, nhưng nghĩ lib\netstandard1.0, lib\netstandard1.1nhóm phụ thuộc ). Vì vậy, nếu dự án của bạn nhắm mục tiêu netstandard1.3, bạn sẽ nhận được 1,3 tham chiếu assembly. Nếu bạn nhắm mục tiêu netstandard1.6, bạn sẽ nhận được các hội đồng tham chiếu 1.6.

Nếu bạn đang tạo một ứng dụng, bạn không thể nhắm mục tiêu Tiêu chuẩn .NET. Nó không có ý nghĩa - bạn không thể chạy trên một thông số kỹ thuật. Thay vào đó, bạn nhắm mục tiêu các nền tảng cụ thể, chẳng hạn như net452hoặc netcoreapp1.1. NuGet biết ánh xạ giữa các nền tảng này và netstandardbiệt danh khung mục tiêu, do đó, biết lib\netstandardX.Xthư mục nào tương thích với nền tảng mục tiêu của bạn. Nó cũng biết rằng các phụ thuộc của NETStandard.Libraryđược thỏa mãn bởi nền tảng mục tiêu, vì vậy sẽ không kéo thêm bất kỳ hội đồng nào khác.

Tương tự, khi tạo một ứng dụng .NET Core độc ​​lập, các hội đồng triển khai .NET Standard được sao chép với ứng dụng của bạn. Tham chiếu tới NETStandard.Librarykhông mang lại bất kỳ ứng dụng mới nào khác.

Lưu ý rằng điều đó dotnet publishsẽ tạo ra một ứng dụng độc lập , nhưng nó hiện không thực hiện việc cắt tỉa và sẽ xuất bản tất cả các tập hợp. Điều này sẽ được xử lý tự động bằng công cụ , vì vậy, một lần nữa, việc cắt bớt các phần phụ thuộc trong thư viện của bạn sẽ không hữu ích ở đây.

Nơi duy nhất tôi có thể tưởng tượng nơi có thể giúp xóa NETStandard.Librarytham chiếu là nếu bạn đang nhắm mục tiêu một nền tảng không hỗ trợ Tiêu chuẩn .NET và bạn có thể tìm thấy một gói từ Tiêu chuẩn .NET nơi tất cả các phụ thuộc bắc cầu có thể chạy trên nền tảng mục tiêu của bạn. Tôi nghi ngờ không có nhiều gói phù hợp với hóa đơn đó.


Nếu bạn thực hiện dotnet publishvới một thời gian chạy cụ thể, nó sẽ mang lại tất cả các phụ thuộc, bao gồm dotnet.exe (hoặc tương đương Linux / OS X của nó). Đó phải là một triển khai hoàn toàn độc lập vào thời điểm đó. Kiểm tra các kết quả cho một dự án đơn vị kiểm tra: gist.github.com/bradwilson/6cc5a8fdfa18230aa6c99b851fb85c01
Brad Wilson

4
Tôi nghĩ rằng đoạn cuối chính xác là vấn đề ... và nếu nó không phải là vấn đề, tại sao chúng ta lại cần các phiên bản netstandard1.x khác nhau? Nếu mọi nền tảng đều có mọi thứ trong netstandard1.6, tại sao thậm chí còn netstandard1.0 như một khái niệm? Đó là một phần khó hiểu đối với tôi.
Jon Skeet

1
Và danh sách các nền tảng hỗ trợ Tiêu chuẩn .NET không bao gồm BẤT KỲ trong số nhiều nền tảng Unity.
citizenmatt

1
(Sự kiên nhẫn của bạn được nhiều đánh giá cao, btw Tôi thực sự hy vọng điều này sẽ có ý nghĩa tất cả, và rằng nó sẽ giúp ích cho nhiều người, nhiều nhà phát triển ....)
Jon Skeet

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.