依赖于元包的网络标准库的应用程序含义是什么?

假设我有一个类库,我想以 netstandard 1.3为目标,但也使用 BigInteger。下面是一个简单的例子——唯一的源文件是 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);
}
}

回到 project.json的世界,我将在 frameworks部分中以 netstandard1.3为目标,并明确依赖于 System.Runtime.Numerics,例如4.0.1版本。我创建的 nuget 包将只列出这个依赖项。

在基于 csproj 的 dotnet 工具(我使用的是命令行工具的 v1.0.1版本)的全新世界中,当瞄准 netstandard1.3时,有一个 隐式元包包引用NETStandard.Library 1.6.1。这意味着我的项目文件非常小,因为它不需要明确的依赖关系:

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

... 但是生产的 Nuget 软件包依赖于 NETStandard.Library,这表明为了使用我的小库,你需要 一切在那里。

原来我可以使用 DisableImplicitFrameworkReferences禁用这个功能,然后再次手动添加依赖项:

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

现在我的 NuGet 软件包说明了它所依赖的东西。

那么对于我的图书馆的消费者来说,到底有什么不同呢?如果有人试图在 UWP 应用程序中使用它,那么第二种“修剪过的”依赖形式是否意味着最终的应用程序将变得更小?

通过没有清楚地记录 DisableImplicitFrameworkReferences(就我所见,我读过关于它的 在一期杂志上) ,以及在创建项目时默认使用隐式依赖,微软是仅仅依赖于元包的 鼓舞人心用户——但是我怎么能确定在我创建类库包时这不会有缺点呢?

7137 次浏览

The team used to recommend figuring out what the slimmest package set was. They no longer do this, and recommend people just bring in NETStandard.Library instead (in the case of an SDK-style project, this will be done automatically for you).

I've never gotten a totally straight forward answer as to why that was, so allow me to make some educated guesses.

The primary reason is likely to be that it allows them to hide the differences in versions of the dependent libraries that you would otherwise be required to track yourself when changing target frameworks. It's also a much more user friendly system with the SDK-based project files, because you frankly don't need any references to get a decent chunk of the platform (just like you used to with the default references in Desktop-land, especially mscorlib).

By pushing the meta-definition of what it means to be a netstandard library, or a netcoreapp application into the appropriate NuGet package, they don't have to build any special knowledge into the definition of those things as Visual Studio (or dotnet new) sees them.

Static analysis could be used during publishing to limit the shipped DLLs, which is something they do today when doing native compilation for UWP (albeit with some caveats). They don't do that today for .NET Core, but I presume it's an optimization they've considered (as well as supporting native code).

There's nothing stopping you from being very selective, if you so choose. I believe you'll find that you're nearly the only one doing it, which also defeats the purpose (since it'll be assumed everybody is bringing in NETStandard.Library or Microsoft.NETCore.App).

You shouldn't need to disable the implicit reference. All platforms that the library will be able to run on will already have the assemblies that the NETStandard.Library dependency would require.

The .NET Standard Library is a specification, a set of reference assemblies that you compile against that provides a set of APIs that are guaranteed to exist on a know set of platforms and versions of platforms, such as .NET Core or the .NET Framework. It is not an implementation of these assemblies, just enough of the API shape to allow the compiler to successfully build your code.

The implementation for these APIs are provided by a target platform, such as .NET Core, Mono or .NET Framework. They ship with the platform, because they are an essential part of the platform. So there is no need to specify a smaller dependency set - everything's already there, you won't change that.

The NETStandard.Library package provides these reference assemblies. One point of confusion is the version number - the package is version 1.6.1, but this does not mean ".NET Standard 1.6". It's just the version of the package.

The version of the .NET Standard you're targeting comes from the target framework you specify in your project.

If you're creating a library and want it to run on .NET Standard 1.3, you'd reference the NETStandard.Library package, currently at version 1.6.1. But more importantly, your project file would target netstandard1.3.

The NETStandard.Library package will give you a different set of reference assemblies depending on your target framework moniker (I'm simplifying for brevity, but think lib\netstandard1.0, lib\netstandard1.1 and dependency groups). So if your project targets netstandard1.3, you'll get the 1.3 reference assemblies. If you target netstandard1.6, you'll get the 1.6 reference assemblies.

If you're creating an application, you can't target the .NET Standard. It doesn't make sense - you can't run on a specification. Instead, you target concrete platforms, such as net452 or netcoreapp1.1. NuGet knows the mapping between these platforms and the netstandard target framework monikers, so knows which lib\netstandardX.X folders are compatible with your target platform. It also knows that the dependencies of NETStandard.Library are satisfied by the target platform, so won't pull in any other assemblies.

Similarly, when creating a standalone .NET Core app, the .NET Standard implementation assemblies are copied with your app. The reference to NETStandard.Library does not bring in any other new apps.

Note that dotnet publish will create a standalone application, but it won't doesn't currently do trimming, and will publish all assemblies. This will be handled automatically by tooling, so again, trimming dependencies in your library won't help here.

The only place I can imagine where it might help to remove the NETStandard.Library reference is if you are targeting a platform that doesn't support the .NET Standard, and you can find a package from the .NET Standard where all of the transitive dependencies can run on your target platform. I suspect there aren't many packages that would fit that bill.

In the past, we've given developers the recommendation to not reference the meta package (NETStandard.Library) from NuGet packages but instead reference individual packages, like System.Runtime and System.Collections. The rationale was that we thought of the meta package as a shorthand for a bunch of packages that were the actual atomic building blocks of the .NET platform. The assumption was: we might end up creating another .NET platform that only supports some of these atomic blocks but not all of them. Hence, the fewer packages you reference, the more portable you'd be. There were also concerns regarding how our tooling deals with large package graphs.

Moving forward, we'll simplify this:

  1. .NET Standard is an atomic building block. In other words, new platforms aren't allowed to subset .NET Standard -- they have to implement all of it.

  2. We're moving away from using packages to describe our platforms, including .NET Standard.

This means, you'll not have to reference any NuGet packages for .NET Standard anymore. You expressed your dependency with the lib folder, which is exactly how it has worked for all other .NET platforms, in particular .NET Framework.

However, right now our tooling will still burn in the reference to NETStandard.Library. There is no harm in that either, it will just become redundant moving forward.

I'll update the FAQ on the .NET Standard repo to include this question.

Update: This question is now part of the FAQ.