Какое значение имеет применение библиотеки netstandard в зависимости от метапакета?

100

Предположим, у меня есть библиотека классов, которую я хочу нацелить на netstandard1.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, я бы нацелился netstandard1.3на этот frameworksраздел и имел явную зависимость System.Runtime.Numerics, например, от версии 4.0.1. В создаваемом мной пакете nuget будет перечислена только эта зависимость.

В дивном новом мире csproj основе Dotnet инструментов (я использую v1.0.1 из инструментов командной строки) есть более подразумеваемая ссылка метапакета пакета для NETStandard.Library 1.6.1при ориентации netstandard1.3. Это означает, что мой файл проекта очень маленький, потому что ему не нужна явная зависимость:

<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четко (насколько я видел; я читал об этом в выпуске ) и делая неявную зависимость значением по умолчанию при создании проекта, Microsoft поощряет пользователей просто полагаться на метапакет - но как я могу быть уверены, что у меня нет недостатков, когда я создаю пакет библиотеки классов?

Джон Скит
источник
3
Следует отметить, что сейчас ведется работа по обрезке зависимостей . В настоящее время размер Hello World!автономного приложения уменьшен до <10 МБ.
ABarney,
@ABarney 10 МБ по-прежнему слишком много по сравнению с размером < 20 КБ классического проекта .NET Framework C # hello-world.
Дай

Ответы:

76

В прошлом мы рекомендовали разработчикам не ссылаться на метапакет ( NETStandard.Library) из пакетов NuGet, а вместо этого ссылаться на отдельные пакеты, такие как System.Runtimeи System.Collections. Обоснованием было то, что мы думали о метапакете как об сокращении для группы пакетов, которые фактически являлись атомарными строительными блоками платформы .NET. Предполагалось, что мы можем создать другую платформу .NET, которая поддерживает только некоторые из этих атомарных блоков, но не все из них. Следовательно, чем меньше пакетов вы ссылаетесь, тем более портативным вы будете. Были также опасения относительно того, как наши инструменты работают с большими графами пакетов.

В дальнейшем мы упростим это:

  1. .NET Standard - это атомарный строительный блок . Другими словами, новым платформам не разрешается подмножество .NET Standard - они должны реализовать все это.

  2. Мы уходим от использования пакетов для описания наших платформ , включая .NET Standard.

Это означает, что вам больше не придется ссылаться на какие-либо пакеты NuGet для .NET Standard. Вы выразили свою зависимость с помощью папки lib, и именно так она работала для всех других платформ .NET, в частности .NET Framework.

Однако сейчас наш инструментарий по-прежнему будет гореть в ссылке на NETStandard.Library. В этом тоже нет вреда, просто в дальнейшем это станет лишним.

Я обновлю FAQ в репозитории .NET Standard, чтобы включить этот вопрос.

Обновление : этот вопрос теперь является частью FAQ .

Иммо Ландверт
источник
9
Спасибо - в этом есть большой смысл. Я думаю, что был частично сбит с толку, потому что в мире project.json мне приходилось добавлять зависимости, даже когда пакеты логически являлись частью фреймворка, на который я нацелен (например, System.Runtime.Numerics для netstandard1.3) - поэтому я подумал, что NETStandard.Library был действительно втягивает их как зависимости.
Джон Скит
2
Облом. Мне понравилась идея ссылаться на пакеты, потому что казалось, что я могу ссылаться только на то, что хочу, а не на всю «кухонную раковину». Может я не правильно об этом думаю?
Нейт Барбеттини,
3
Вы не обязательно думаете об этом неправильно, но вопрос в том, почему вас это волнует. Важно понимать, что платформа не может быть бесконечно компонуемой. Когда вы работаете в общей среде фреймворка (.NET Framework, Mono, .NET Core), все эти биты все равно присутствуют. Если вы создаете автономное приложение (Xamarin, Mono / .NET Core с компоновщиком), мы можем автоматически обрезать его в зависимости от вашего фактического использования. Так что в любом случае вы ничего не потеряете, не ссылаясь на более мелкие блоки.
Immo Landwerth
3
А как насчет того, чтобы компилятор применял такие правила, как запрет на выполнение разработчиком HTTP-запроса в проекте бизнес-логики, не разрешая этот пакет - пустая трата усилий?
Дэвид Ферретти
Не обязательно, но как обеспечить его соблюдение, т.е. что мешает разработчику добавить ссылку? Я бы написал анализатор, который вводится во время сборки для обеспечения соблюдения правил уровня и вызывает ошибки на машине сборки.
Immo Landwerth
19

Команда раньше рекомендовала выяснить, какой комплект был самый тонкий. Они больше этого не делают и рекомендуют вместо этого просто использовать NETStandard.Library (в случае проекта в стиле SDK это будет сделано автоматически за вас).

Я так и не получил однозначного ответа на вопрос, почему это так, поэтому позвольте мне сделать несколько обоснованных предположений.

Основная причина, вероятно, заключается в том, что это позволяет им скрыть различия в версиях зависимых библиотек, которые в противном случае вам пришлось бы отслеживать при смене целевых фреймворков. Это также гораздо более удобная система с файлами проекта на основе SDK, потому что вам, честно говоря, не нужны никакие ссылки, чтобы получить приличный кусок платформы (как вы привыкли со ссылками по умолчанию в Desktop-land, особенно mscorlib ).

Помещая мета-определение того, что значит быть netstandardбиблиотекой или netcoreappприложением в соответствующий пакет NuGet, им не нужно встраивать какие-либо специальные знания в определение этих вещей, как dotnet newих видит Visual Studio (или ).

Статический анализ можно использовать во время публикации, чтобы ограничить поставляемые библиотеки DLL, что они и делают сегодня при собственной компиляции для UWP (хотя и с некоторыми оговорками). Сегодня они не делают этого для .NET Core, но я предполагаю, что это оптимизация, которую они рассмотрели (а также поддержка собственного кода).

Ничто не мешает вам быть очень избирательным, если вы того пожелаете. Я полагаю, вы обнаружите, что это делаете вы почти единственный, что также противоречит цели (поскольку предполагается, что все вносят NETStandard.Libraryили Microsoft.NETCore.App).

Брэд Уилсон
источник
6
Я согласен, что это определенно нарушает цель, если существует более одной зависимости, если какая-либо из них вносит NETStandard.Library. Это, конечно, в некоторой степени самореализующееся ... если я полагаюсь на NETStandard.LibraryNoda Time, это означает, что у любой другой библиотеки, построенной на основе Noda Time, нет причин обрезать зависимости и т.д. идем к 2.0), а затем расслабьтесь немного позже, как только будут установлены соглашения - я полагаю, что переход от выборочного к основанному на библиотеке будет неразрывным изменением, но обратное неверно.
Джон Скит,
8

Вам не нужно отключать неявную ссылку. Все платформы, на которых сможет работать библиотека, уже будут иметь сборки, NETStandard.Libraryкоторые потребуются зависимости.

Стандартная библиотека .NET - это спецификация, набор эталонных сборок, с которыми вы компилируете, который предоставляет набор API, которые гарантированно существуют на известном наборе платформ и версиях платформ, таких как .NET Core или .NET Framework. . Это не реализация этих сборок, а только форма API, позволяющая компилятору успешно построить ваш код.

Реализация этих API обеспечивается целевой платформой, например .NET Core, Mono или .NET Framework. Они поставляются вместе с платформой, потому что являются неотъемлемой частью платформы. Таким образом, нет необходимости указывать меньший набор зависимостей - все уже есть, вы не будете это менять.

NETStandard.LibraryПакет предоставляет эти опорные узлы. Одна вещь, вызывающая замешательство, - это номер версии - это версия пакета 1.6.1, но это не означает «.NET Standard 1.6». Это просто версия пакета.

Версия .NET Standard, на которую вы нацелены, исходит из целевой платформы, указанной в вашем проекте.

Если вы создаете библиотеку и хотите, чтобы она работала в .NET Standard 1.3, вы должны сослаться на NETStandard.Libraryпакет, который сейчас находится в версии 1.6.1. Но что более важно, ваш файл проекта будет нацелен на netstandard1.3.

NETStandard.LibraryПакет даст вам другой набор эталонных сборок в зависимости от целевого рамочным прозвища (я упрощая для краткости, но думает lib\netstandard1.0, lib\netstandard1.1и группы зависимостей ). Так что, если ваш проект нацелен netstandard1.3, вы получите эталонные сборки 1.3. Если вы нацелитесь netstandard1.6, вы получите эталонные сборки 1.6.

Если вы создаете приложение, вы не можете настроить таргетинг на .NET Standard. В этом нет смысла - вы не можете работать по спецификации. Вместо этого вы ориентируетесь на конкретные платформы, такие как net452или netcoreapp1.1. NuGet знает соответствие между этими платформами и netstandardименами целевой платформы, поэтому знает, какие lib\netstandardX.Xпапки совместимы с вашей целевой платформой. Он также знает, что зависимости NETStandard.Libraryудовлетворяются целевой платформой, поэтому не будет задействовать какие-либо другие сборки.

Точно так же при создании автономного приложения .NET Core сборки реализации .NET Standard копируются вместе с вашим приложением. Ссылка на NETStandard.Libraryне вводит никаких других новых приложений.

Обратите внимание, что dotnet publishбудет создано отдельное приложение , но в настоящее время оно не выполняет обрезку и опубликует все сборки. Это будет обрабатываться автоматически с помощью инструментов , поэтому, опять же, обрезка зависимостей в вашей библиотеке здесь не поможет.

Единственное место, где я могу представить, где это может помочь удалить NETStandard.Libraryссылку, - это если вы ориентируетесь на платформу, которая не поддерживает .NET Standard, и вы можете найти пакет из .NET Standard, в котором могут выполняться все транзитивные зависимости. на вашей целевой платформе. Я подозреваю, что не так много пакетов подошло бы под этот счет.

гражданин
источник
Если вы сделаете это dotnet publishдля определенной среды выполнения, она внесет все зависимости, включая dotnet.exe (или его эквивалент для Linux / OS X). На тот момент это должно быть полностью автономное развертывание. Ознакомьтесь с результатами проекта модульного тестирования: gist.github.com/bradwilson/6cc5a8fdfa18230aa6c99b851fb85c01
Брэд Уилсон,
4
Я думаю, что последний абзац как раз и является проблемой ... и если бы это не было проблемой, зачем нам вообще нужны разные версии netstandard1.x? Если на каждой платформе есть все, что есть в netstandard1.6, зачем вообще использовать netstandard1.0 в качестве концепции? Это сбивает меня с толку.
Джон Скит,
1
И в список платформ, поддерживающих .NET Standard, не входит НИКАКАЯ из многих платформ Unity.
Citizenmatt
1
(Мы очень ценим ваше терпение, кстати. Я очень надеюсь, что все это будет иметь смысл и поможет многим, многим разработчикам ...)
Джон Скит,