{"id":29900,"date":"2020-09-15T17:06:05","date_gmt":"2020-09-16T00:06:05","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/dotnet\/?p=29900"},"modified":"2024-06-26T10:44:19","modified_gmt":"2024-06-26T17:44:19","slug":"the-future-of-net-standard","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/dotnet\/the-future-of-net-standard\/","title":{"rendered":"The future of .NET Standard"},"content":{"rendered":"<p>Since <a href=\"https:\/\/devblogs.microsoft.com\/dotnet\/introducing-net-5\/\">.NET 5 was announced<\/a>, many of you have asked what this means\nfor .NET Standard and whether it will still be relevant. In this post, I&#8217;m going\nto explain how .NET 5 improves code sharing and replaces .NET Standard. I&#8217;ll\nalso cover the cases where you still need .NET Standard.<\/p>\n<h2>For the impatient: TL;DR<\/h2>\n<p>.NET 5 will be a single product with a uniform set of capabilities and APIs that\ncan be used for Windows desktop apps, cross-platform mobile apps, console apps,\ncloud services, and websites:<\/p>\n<p><div style=\"width: 1424px;\" class=\"wp-video\"><video class=\"wp-video-shortcode\" id=\"video-29900-1\" width=\"1424\" height=\"602\" preload=\"metadata\" controls=\"controls\"><source type=\"video\/mp4\" src=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2020\/09\/net5-vision.mp4?_=1\" \/><a href=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2020\/09\/net5-vision.mp4\">https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2020\/09\/net5-vision.mp4<\/a><\/video><\/div><\/p>\n<p>To better reflect this, we&#8217;ve updated the <a href=\"https:\/\/github.com\/dotnet\/designs\/blob\/master\/accepted\/2020\/net5\/net5.md\">target framework names (TFMs)<\/a>:<\/p>\n<ul>\n<li><code>net5.0<\/code>. This is for code that runs everywhere. It combines and replaces the\n<code>netcoreapp<\/code> and <code>netstandard<\/code> names. This TFM will generally only include\ntechnologies that work cross-platform (except for pragmatic concessions, like we\nalready did in .NET Standard).<\/li>\n<li><code>net5.0-windows<\/code> (and later <code>net6.0-android<\/code> and <code>net6.0-ios<\/code>). These TFMs\nrepresent OS-specific flavors of .NET 5 that include <code>net5.0<\/code> plus OS-specific\nfunctionality.<\/li>\n<\/ul>\n<p>We won&#8217;t be releasing a new version of .NET Standard, but .NET 5 and all future\nversions will continue to support .NET Standard 2.1 and earlier. You should\nthink of <code>net5.0<\/code> (and future versions) as the foundation for sharing code\nmoving forward.<\/p>\n<p>And since net5.0 is the shared base for all these new TFMs, that means that the\nruntime, library, and new language features are coordinated around this version\nnumber. For example, in order to use C# 9, you need to use <code>net5.0<\/code> or\n<code>net5.0-windows<\/code>.<\/p>\n<h2>What you should target<\/h2>\n<p>.NET 5 and all future versions will always support .NET Standard 2.1 and\nearlier. The only reason to retarget from .NET Standard to .NET 5 is to gain\naccess to more runtime features, language features, or APIs. So, you can think of\n.NET 5 as .NET Standard vNext.<\/p>\n<p>What about new code? Should you still start with .NET Standard 2.0 or should you\ngo straight to .NET 5? It depends.<\/p>\n<ul>\n<li><strong>App components<\/strong>. If you&#8217;re using libraries to break down your application\ninto several components, my recommendation is to use <code>netX.Y<\/code> where <code>X.Y<\/code> is\nthe lowest number of .NET that your application (or applications) are\ntargeting. For simplicity, you probably want all projects that make up your\napplication to be on the same version of .NET because it means you can assume\nthe same BCL features everywhere.<\/li>\n<li><strong>Reusable libraries<\/strong>. If you&#8217;re building reusable libraries that you plan on\nshipping on NuGet, you&#8217;ll want to consider the trade-off between reach and\navailable feature set. .NET Standard 2.0 is the highest version of .NET\nStandard that is supported by .NET Framework, so it will give you the most\nreach, while also giving you a fairly large feature set to work with. We&#8217;d\ngenerally recommend against targeting .NET Standard 1.x as it&#8217;s not worth the\nhassle anymore. If you don&#8217;t need to support .NET Framework, then you can\neither go with .NET Standard 2.1 or .NET 5. Most code can probably skip .NET\nStandard 2.1 and go straight to .NET 5.<\/li>\n<\/ul>\n<p>So, what should you do? My expectation is that widely used libraries will end up\nmulti-targeting for both .NET Standard 2.0 and .NET 5: supporting .NET Standard\n2.0 gives you the most reach while supporting .NET 5 ensures you can leverage\nthe latest platform features for customers that are already on .NET 5.<\/p>\n<p>In a couple of years, the choice for reusable libraries will only involve the\nversion number of <code>netX.Y<\/code>, which is basically how building libraries for .NET\nhas always worked &#8212; you generally want to support some older version in order\nto ensure you get the most reach.<\/p>\n<p>To summarize:<\/p>\n<ul>\n<li>Use <code>netstandard2.0<\/code> to share code between .NET Framework and all other\nplatforms.<\/li>\n<li>Use <code>netstandard2.1<\/code> to share code between Mono, Xamarin, and .NET Core 3.x.<\/li>\n<li>Use <code>net5.0<\/code> for code sharing moving forward.<\/li>\n<\/ul>\n<h2>Problems with .NET Standard<\/h2>\n<p>.NET Standard has made it much easier to create libraries that work on all .NET\nplatforms. But there are still three problems with .NET Standard:<\/p>\n<ol>\n<li><strong>It <a href=\"https:\/\/github.com\/dotnet\/standard\/tree\/release\/3.0\/docs\/governance#process\">versions slowly<\/a><\/strong>, which means you can&#8217;t easily use the\nlatest features.<\/li>\n<li><strong>It needs a <a href=\"https:\/\/dotnet.microsoft.com\/platform\/dotnet-standard#versions\">decoder ring<\/a><\/strong> to map versions to .NET\nimplementations.<\/li>\n<li><strong>It <a href=\"https:\/\/github.com\/dotnet\/standard\/blob\/release\/3.0\/docs\/faq.md#why-do-you-include-apis-that-dont-work-everywhere\">exposes platform-specific features<\/a><\/strong>, which means you can&#8217;t\nstatically validate whether your code is truly portable.<\/li>\n<\/ol>\n<p>Let&#8217;s see how .NET 5 will address all three issues.<\/p>\n<h2>Problem 1: .NET Standard versions slowly<\/h2>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/dotnet\/introducing-net-standard\/\">.NET Standard was designed<\/a> at a time where the .NET platforms weren&#8217;t\nconverged at the implementation level. This made writing code that needs to work\nin different environments hard, because different workloads used different .NET\nimplementations.<\/p>\n<p>The goal of .NET Standard was to unify the feature set of the base class library\n(BCL), so that you can write a single library that can run everywhere. And this\nhas served us well: .NET Standard is supported by over 77% of the top 1000\npackages. And if we look at all packages on NuGet.org that have been updated in\nthe last 6 months, the adoption is at 58%.<\/p>\n<p><img decoding=\"async\" class=\"aligncenter size-full wp-image-29904\" src=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2020\/09\/chart-all.png\" alt=\"#Packages supporting .NET Standard\" width=\"2000\" height=\"1125\" srcset=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2020\/09\/chart-all.png 2000w, https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2020\/09\/chart-all-300x169.png 300w, https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2020\/09\/chart-all-1024x576.png 1024w, https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2020\/09\/chart-all-768x432.png 768w, https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2020\/09\/chart-all-1536x864.png 1536w\" sizes=\"(max-width: 2000px) 100vw, 2000px\" \/><\/p>\n<p>But standardizing the API set alone creates a tax. It requires coordination\nwhenever we&#8217;re adding new APIs &#8212; which happens all the time. The .NET\nopen-source community (which includes the .NET team) keeps innovating in the BCL\nby providing new language features, usability improvements, new cross-cutting\nfeatures such as <code>Span&lt;T&gt;<\/code>, or supporting new data formats or networking\nprotocols.<\/p>\n<p>And while we can provide new types as NuGet packages, we can&#8217;t provide new APIs\non existing types this way. So, in the general sense, innovation in the BCL\nrequires shipping a new version of .NET Standard.<\/p>\n<p>Up until .NET Standard 2.0, this wasn&#8217;t really an issue because we only\nstandardized <em>existing<\/em> APIs. But in .NET Standard 2.1, we standardized brand new\nAPIs and that&#8217;s where we saw quite a bit of friction.<\/p>\n<p>Where does this friction come from?<\/p>\n<p>.NET Standard is an API set that all .NET implementations have to support, so\nthere is an <a href=\"https:\/\/github.com\/dotnet\/standard\/tree\/release\/3.0\/docs\/governance#process\">editorial aspect<\/a> to it in that all APIs must be\nreviewed by the <a href=\"https:\/\/github.com\/dotnet\/standard\/blob\/release\/3.0\/docs\/governance\/board.md\">.NET Standard review board<\/a>. The board is comprised\nof .NET platform implementers as well as representatives of the .NET community.\nThe goal is to only standardize APIs that we can truly implement in all current\n<em>and<\/em> future .NET platforms. These reviews are necessary because there are\ndifferent implementations of the .NET stack, with different constraints.<\/p>\n<p>We predicted this type of friction, which is why we said early on that .NET\nStandard <a href=\"https:\/\/github.com\/dotnet\/standard\/tree\/release\/3.0\/docs\/governance#process\">will only standardize APIs<\/a> that were already shipped in at\nleast one .NET implementation. This seems reasonable at first, but then you\nrealize that .NET Standard can&#8217;t ship very frequently. So, if a feature\nmisses a particular release, you might have to wait for a couple of years before\nit&#8217;s even available and potentially even longer until this version of .NET\nStandard is widely supported.<\/p>\n<p>We felt for some features that opportunity loss was too high, so we did\nunnatural acts to standardize APIs that weren&#8217;t shipped yet (such as\n<code>IAsyncEnumerable&lt;T&gt;<\/code>). Doing this for all features was simply too expensive,\nwhich is why quite a few of them still missed the .NET Standard 2.1 train (such\nas the new hardware intrinsics).<\/p>\n<p>But what if there was a single code base? And what if that code base would have\nto support all the aspects that make .NET implementations differ today, such\nas supporting both just-in-time (JIT) compilation and ahead-of-time\n(AOT) compilation?<\/p>\n<p>Instead of doing these reviews as an afterthought, we&#8217;d make all these aspects\npart of the feature design, right from the start. In such a world, the\nstandardized API set is, by construction, the common API set. When a feature is\nimplemented, it would already be available for everyone because the code base is\nshared.<\/p>\n<h2>Problem 2: .NET Standard needs a decoder ring<\/h2>\n<p>Separating the API set from its implementation doesn&#8217;t just slow down the\navailability of APIs. It also means that we need to <a href=\"https:\/\/dotnet.microsoft.com\/platform\/dotnet-standard#versions\">map .NET Standard versions\nto their implementations<\/a>. As someone who had to explain this table to\nmany people over time, I&#8217;ve come to appreciate just how complicated this\nseemingly simple idea is. We&#8217;ve tried our best to make it easier, but in the\nend, it&#8217;s just inherent complexity because the API set and the implementations\nare shipped independently.<\/p>\n<p>We have unified the .NET platforms by adding yet another synthetic platform\nbelow them all that represents the common API set. In a very real sense, this\n<a href=\"https:\/\/xkcd.com\/927\">XKCD-inspired comic<\/a> is spot on:<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2014\/12\/7725.Pic6_.png\" alt=\"How .NET platforms proliferate\" \/><\/p>\n<p>We can&#8217;t solve this problem without truly merging some rectangles in our layer\ndiagram, which is what .NET 5 does: it provides a unified implementation where\nall parties build on the same foundation and thus get the same API shape and\nversion number.<\/p>\n<h2>Problem 3: .NET Standard exposes platform-specific APIs<\/h2>\n<p>When we designed .NET Standard, <a href=\"https:\/\/github.com\/dotnet\/standard\/blob\/release\/3.0\/docs\/faq.md#why-do-you-include-apis-that-dont-work-everywhere\">we had to make pragmatic\nconcessions<\/a> in order to avoid breaking the library ecosystem too\nmuch. That is, we had to include some Windows-only APIs (such as file system\nACLs, the registry, WMI, and so on). Moving forward, we will avoid adding\nplatform-specific APIs to <code>net5.0<\/code>, <code>net6.0<\/code> and future versions. However, it&#8217;s impossible for us to predict\nthe future. For example, with Blazor WebAssembly we have recently added a new\nenvironment where .NET runs and some of the otherwise cross-platform APIs (such\nas threading or process control) can&#8217;t be supported in the browser&#8217;s sandbox.<\/p>\n<p>Many of you have complained that these kind of APIs feel like &#8220;landmines&#8221; &#8211; the\ncode compiles without errors and thus appears to being portable to any platform,\nbut when running on a platform that doesn&#8217;t have an implementation for the given\nAPI, you get runtime errors.<\/p>\n<p>Starting with .NET 5, we&#8217;re <a href=\"https:\/\/devblogs.microsoft.com\/dotnet\/automatically-find-latent-bugs-in-your-code-with-net-5\/\">shipping analyzers and code fixers<\/a>\nwith the SDK that are on by default. This includes the <a href=\"https:\/\/github.com\/dotnet\/designs\/pull\/110\">platform compatibility\nanalyzer<\/a> that detects unintentional use of APIs that\naren&#8217;t supported on the platforms you intend to run on. This feature replaces\nthe <code>Microsoft.DotNet.Analyzers.Compatibility<\/code> NuGet package.<\/p>\n<p>Let&#8217;s first look at Windows-specific APIs.<\/p>\n<h3>Dealing with Windows-specific APIs<\/h3>\n<p>When you create a project targeting <code>net5.0<\/code>, you can reference the\n<code>Microsoft.Win32.Registry<\/code> package. But when you start using it, you&#8217;ll get the\nfollowing warnings:<\/p>\n<pre><code class=\"language-C#\">private static string GetLoggingDirectory()\r\n{\r\n    using (RegistryKey key = Registry.CurrentUser.OpenSubKey(@\"Software\\Fabrikam\"))\r\n    {\r\n        if (key?.GetValue(\"LoggingDirectoryPath\") is string configuredPath)\r\n            return configuredPath;\r\n    }\r\n\r\n    string exePath = Process.GetCurrentProcess().MainModule.FileName;\r\n    string folder = Path.GetDirectoryName(exePath);\r\n    return Path.Combine(folder, \"Logging\");\r\n}\r\n<\/code><\/pre>\n<pre class=\"prettyprinted\">CA1416: 'RegistryKey.OpenSubKey(string)' is supported on 'windows'\r\nCA1416: 'Registry.CurrentUser' is supported on 'windows'\r\nCA1416: 'RegistryKey.GetValue(string?)' is supported on 'windows'\r\n<\/pre>\n<p>You have three options on how you can address these warnings:<\/p>\n<ol>\n<li><strong>Guard the call<\/strong>. You can check whether you&#8217;re running on Windows before\ncalling the API by using <code>OperatingSystem.IsWindows()<\/code>.<\/li>\n<li><strong>Mark the call as Windows-specific<\/strong>. In some cases, it might make sense to\nmark the calling member as platform-specific via\n<code>[SupportedOSPlatform(\"windows\")]<\/code>.<\/li>\n<li><strong>Delete the code<\/strong>. Generally not what you want because it means you lose\nfidelity when your code is used by Windows users, but for cases where a\ncross-platform alternative exists, you&#8217;re likely better off using that over\nplatform-specific APIs. For example, instead of using the registry, you could\nuse an XML configuration file.<\/li>\n<li><strong>Suppress the warning<\/strong>. You can of course cheat and simply suppress the\nwarning, either via <code>.editorconfig<\/code> or <code>#pragma warning disable<\/code>. However,\nyou should prefer options (1) and (2) when using platform-specific APIs.<\/li>\n<\/ol>\n<p>To <strong>guard the call<\/strong>, use the new static methods on the\n<a href=\"https:\/\/docs.microsoft.com\/dotnet\/api\/system.operatingsystem\">System.OperatingSystem<\/a> class, for example:<\/p>\n<pre><code class=\"language-C#\">private static string GetLoggingDirectory()\r\n{\r\n    if (OperatingSystem.IsWindows())\r\n    {\r\n        using (RegistryKey key = Registry.CurrentUser.OpenSubKey(@\"Software\\Fabrikam\"))\r\n        {\r\n            if (key?.GetValue(\"LoggingDirectoryPath\") is string configuredPath)\r\n                return configuredPath;\r\n        }\r\n    }\r\n\r\n    string exePath = Process.GetCurrentProcess().MainModule.FileName;\r\n    string folder = Path.GetDirectoryName(exePath);\r\n    return Path.Combine(folder, \"Logging\");\r\n}\r\n<\/code><\/pre>\n<p>To <strong>mark your code as Windows-specific<\/strong>, apply the new\n<code>SupportedOSPlatform<\/code> attribute:<\/p>\n<pre><code class=\"language-C#\">[SupportedOSPlatform(\"windows\")]\r\nprivate static string GetLoggingDirectory()\r\n{\r\n    using (RegistryKey key = Registry.CurrentUser.OpenSubKey(@\"Software\\Fabrikam\"))\r\n    {\r\n        if (key?.GetValue(\"LoggingDirectoryPath\") is string configuredPath)\r\n            return configuredPath;\r\n    }\r\n\r\n    string exePath = Process.GetCurrentProcess().MainModule.FileName;\r\n    string folder = Path.GetDirectoryName(exePath);\r\n    return Path.Combine(folder, \"Logging\");\r\n}\r\n<\/code><\/pre>\n<p>In both cases, the warnings for using the registry will disappear.<\/p>\n<p>The key difference is that in the second example the analyzer will now issue\nwarnings for the call sites of <code>GetLoggingDirectory()<\/code> because it is now\nconsidered to be a Windows-specific API. In other words, you forward the\nrequirement of doing the platform check to your callers.<\/p>\n<p>The <code>[SupportedOSPlatform]<\/code> attribute can be applied to the member, type, or\nassembly level. This attribute is also used by the BCL itself. For example, the\nassembly <code>Microsoft.Win32.Registry<\/code> has this attribute applied, which is how\nthe analyzer knows that the registry is a Windows-specific API in the first\nplace.<\/p>\n<p>Note that if you target <code>net5.0-windows<\/code>, this attribute is automatically applied\nto your assembly. That means using Windows-specific APIs from <code>net5.0-windows<\/code>\nwill never generate any warnings because your entire assembly is considered to\nbe Windows-specific.<\/p>\n<h3>Dealing with APIs that are unsupported in Blazor WebAssembly<\/h3>\n<p>Blazor WebAssembly projects run inside the browser sandbox, which constrains\nwhich APIs you can use. For example, while thread and process creation are both\ncross-platform APIs, we can&#8217;t make these APIs work in Blazor WebAssembly, which\nmeans they throw <code>PlatformNotSupportedException<\/code>. We have marked these APIs with\n<code>[UnsupportedOSPlatform(\"browser\")]<\/code>.<\/p>\n<p>Let&#8217;s say you copy &amp; paste the <code>GetLoggingDirectory()<\/code> method into a Blazor WebAssembly\napplication.<\/p>\n<pre><code class=\"language-C#\">private static string GetLoggingDirectory()\r\n{\r\n    \/\/...\r\n\r\n    string exePath = Process.GetCurrentProcess().MainModule.FileName;\r\n    string folder = Path.GetDirectoryName(exePath);\r\n    return Path.Combine(folder, \"Logging\");\r\n}\r\n<\/code><\/pre>\n<p>You&#8217;ll get the following warnings:<\/p>\n<pre class=\"prettyprinted\">CA1416 'Process.GetCurrentProcess()' is unsupported on 'browser'\r\nCA1416 'Process.MainModule' is unsupported on 'browser'\r\n<\/pre>\n<p>To deal with these warnings, you have basically the same options\nas with Windows-specific APIs.<\/p>\n<p>You can <strong>guard the call<\/strong>:<\/p>\n<pre><code class=\"language-C#\">private static string GetLoggingDirectory()\r\n{\r\n    \/\/...\r\n\r\n    if (!OperatingSystem.IsBrowser())\r\n    {\r\n        string exePath = Process.GetCurrentProcess().MainModule.FileName;\r\n        string folder = Path.GetDirectoryName(exePath);\r\n        return Path.Combine(folder, \"Logging\");\r\n    }\r\n    else\r\n    {\r\n        return string.Empty;\r\n    }\r\n}\r\n<\/code><\/pre>\n<p>Or you can mark the member as being unsupported by Blazor WebAssembly:<\/p>\n<pre><code class=\"language-C#\">[UnsupportedOSPlatform(\"browser\")]\r\nprivate static string GetLoggingDirectory()\r\n{\r\n    \/\/...\r\n\r\n    string exePath = Process.GetCurrentProcess().MainModule.FileName;\r\n    string folder = Path.GetDirectoryName(exePath);\r\n    return Path.Combine(folder, \"Logging\");\r\n}\r\n<\/code><\/pre>\n<p>Since the browser sandbox is fairly restrictive, not all class libraries and\nNuGet packages should be expected to work in Blazor WebAssembly. Furthermore,\nthe vast majority of libraries aren&#8217;t expected to support running in Blazor\nWebAssembly either.<\/p>\n<p>That&#8217;s why regular class libraries targeting <code>net5.0<\/code> won&#8217;t see warnings for\nAPIs that are unsupported by Blazor WebAssembly. You have to explicitly indicate\nthat you intend to support your project in Blazor Web Assembly by adding the\n<code>&lt;SupportedPlatform&gt;<\/code> item to your project file:<\/p>\n<pre><code class=\"language-XML\">&lt;Project Sdk=\"Microsoft.NET.Sdk\"&gt;\r\n\r\n  &lt;PropertyGroup&gt;\r\n    &lt;TargetFramework&gt;net5.0&lt;\/TargetFramework&gt;\r\n  &lt;\/PropertyGroup&gt;\r\n  \r\n  &lt;ItemGroup&gt;\r\n    &lt;SupportedPlatform Include=\"browser\" \/&gt;\r\n  &lt;\/ItemGroup&gt;\r\n  \r\n&lt;\/Project&gt;\r\n<\/code><\/pre>\n<p>If you&#8217;re building a Blazor WebAssembly application, you don&#8217;t have to do this\nbecause the <code>Microsoft.NET.Sdk.BlazorWebAssembly<\/code> SDK does this automatically.<\/p>\n<h2>.NET 5 as the combination of .NET Standard &amp; .NET Core<\/h2>\n<p>.NET 5 and subsequent versions will be a single code base that supports desktop\napps, mobile apps, cloud services, websites, and whatever environment .NET will\nrun on tomorrow.<\/p>\n<p>You might think &#8220;hold on, this sounds great, but what if someone wants to create\na completely new implementation&#8221;. That&#8217;s fine too. But virtually nobody will\nstart one from scratch. Most likely, it will be a fork of the current code base\n(<a href=\"https:\/\/github.com\/dotnet\/runtime\">dotnet\/runtime<\/a>). For example, Tizen (the Samsung platform for smart\nappliances) uses a .NET Core with minimal changes and a Samsung-specific app\nmodel on top.<\/p>\n<p>Forking preserves a merge relationship, which allows maintainers to keep pulling\nin new changes from the <a href=\"https:\/\/github.com\/dotnet\/runtime\">dotnet\/runtime<\/a> repo, benefiting from BCL innovations\nin areas unaffected by their changes. That&#8217;s very similar to how Linux distros\nwork.<\/p>\n<p>Granted, there are cases where one might want to create a very different &#8220;kind&#8221;\nof .NET, such as a minimal runtime without the current BCL. But that would\nmean that it couldn&#8217;t leverage the existing .NET library ecosystem anyway, which\nmeans it wouldn&#8217;t have implemented .NET Standard either. We&#8217;re generally not\ninterested in pursuing this direction, but the convergence of .NET Standard and\n.NET Core doesn&#8217;t prevent that nor does it make it any harder.<\/p>\n<h2>.NET versioning<\/h2>\n<p>As a library author, you&#8217;re probably wondering when .NET 5 will be widely\nsupported. Moving forward, we&#8217;ll ship .NET every year in November, with every\nother year being a Long Term Support (LTS) release.<\/p>\n<p>.NET 5 will ship in November 2020 and .NET 6 will ship in November 2021 as an\nLTS. We created this fixed schedule to make it easier for you to plan your\nupdates (if you&#8217;re an app developer) and predict the demand for supported .NET\nversions (if you&#8217;re a library developer).<\/p>\n<p>Thanks to the ability to install .NET Core side-by-side, new versions are\nadopted fairly fast with LTS versions being the most popular. In fact, .NET Core\n3.1 was the fastest adopted .NET version ever.<\/p>\n<p><img decoding=\"async\" class=\"aligncenter size-full wp-image-29909\" src=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2020\/09\/net5-schedule.png\" alt=\".NET 5 Schedule\" width=\"2322\" height=\"1303\" srcset=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2020\/09\/net5-schedule.png 2322w, https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2020\/09\/net5-schedule-300x168.png 300w, https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2020\/09\/net5-schedule-1024x575.png 1024w, https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2020\/09\/net5-schedule-768x431.png 768w, https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2020\/09\/net5-schedule-1536x862.png 1536w, https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2020\/09\/net5-schedule-2048x1149.png 2048w\" sizes=\"(max-width: 2322px) 100vw, 2322px\" \/><\/p>\n<p>The expectation is that every time we ship, we ship all framework names in\nconjunction. For example, it might look something like this:<\/p>\n<table>\n<thead>\n<tr>\n<th>.NET 5<\/th>\n<th>.NET 6<\/th>\n<th>.NET 7<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td><code>net5.0<\/code><\/td>\n<td><code>net6.0<\/code><\/td>\n<td><code>net7.0<\/code><\/td>\n<\/tr>\n<tr>\n<td><\/td>\n<td><code>net6.0-android<\/code><\/td>\n<td><code>net7.0-android<\/code><\/td>\n<\/tr>\n<tr>\n<td><\/td>\n<td><code>net6.0-ios<\/code><\/td>\n<td><code>net7.0-ios<\/code><\/td>\n<\/tr>\n<tr>\n<td><code>net5.0-windows<\/code><\/td>\n<td><code>net6.0-windows<\/code><\/td>\n<td><code>net7.0-windows<\/code><\/td>\n<\/tr>\n<tr>\n<td><code>net5.0-someoldos<\/code><\/td>\n<td><\/td>\n<td><\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>This means that you can generally expect that whatever innovation we did in the\nBCL, you&#8217;re going to be able to use it from all app models, no matter which\nplatform they run on. It also means that libraries shipped for the latest <code>net<\/code>\nframework can always be consumed from all app models, as long as you run the latest\nversion of them.<\/p>\n<p>This model removes the complexity around .NET Standard versioning because each\ntime we ship, you can assume that all platforms are going to support the new\nversion immediately and completely. And we cement this promise by using the\nprefix naming convention.<\/p>\n<p>New versions of .NET might add support for other platforms. For example, we will add support for\nAndroid and iOS, with .NET 6. Conversely, we might stop supporting platforms that\nare no longer relevant. This is illustrated by the pretend <code>net5.0-someoldos<\/code> target framework that doesn&#8217;t exist in\n.NET 6. We have no plans for dropping a platform, but the model supports it. That would be a big deal, isn&#8217;t expected and would be announced long in advance. That&#8217;s the same model we had with .NET Standard, where, for example, there is no\nnew version of Windows Phone that implements a later version of .NET Standard.<\/p>\n<h2>Why there is no TFM for WebAssembly<\/h2>\n<p>We originally considered adding TFM for WebAssembly, such as <code>net5.0-wasm<\/code>. We\ndecided against that for the following reasons:<\/p>\n<ul>\n<li>WebAssembly is more like an instruction set (such as x86 or x64) than like an\noperating system. And we generally don&#8217;t offer divergent APIs between different\narchitectures.<\/li>\n<li>WebAssembly&#8217;s execution model in the browser sandbox is a key differentiator,\nbut we decided that it makes more sense to only model this as a runtime check.\nSimilar to how you check for Windows and Linux, you can use the <code>OperatingSystem<\/code>\ntype. Since this isn&#8217;t about instruction set, the method is called <code>IsBrowser()<\/code>\nrather than <code>IsWebAssembly()<\/code>.<\/li>\n<li>There are <a href=\"https:\/\/docs.microsoft.com\/en-us\/dotnet\/core\/rid-catalog\">runtime identifiers (RID)<\/a> for WebAssembly, called <code>browser<\/code>\nand <code>browser-wasm<\/code>. They allow package authors to deploy different binaries\nwhen targeting WebAssembly in a browser. This is especially useful for native code which needs to be compiled to web assembly beforehand.<\/li>\n<\/ul>\n<p>As described above, we have marked APIs that are unsupported\nin the browser sandbox, such as <code>System.Diagnostics.Process<\/code>. If you use\nthose APIs from inside a browser app, you&#8217;ll get a warning telling you that this\nAPIs is unsupported.<\/p>\n<h2>Summary<\/h2>\n<p><code>net5.0<\/code> is for code that runs everywhere. It combines and replaces the\n<code>netcoreapp<\/code> and <code>netstandard<\/code> names. We also have platform-specific frameworks,\nsuch as <code>net5.0-windows<\/code> (and later also <code>net6.0-android<\/code>, and <code>net6.0-ios<\/code>).<\/p>\n<p>Since there is no difference between the standard and its implementation, you&#8217;ll\nbe able to take advantage of new features much quicker than with .NET Standard.\nAnd due to the naming convention, you&#8217;ll be able to easily tell who can consume\na given library &#8212; without having to consult the .NET Standard version table.<\/p>\n<p>While .NET Standard 2.1 will be the last version of .NET Standard, .NET 5 and\nall future versions will continue to support .NET Standard 2.1 and earlier. You\nshould think of <code>net5.0<\/code> (and future versions) as the foundation for sharing\ncode moving forward.<\/p>\n<p>Happy coding!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Since .NET 5 was announced, many of you have asked what this means for .NET Standard and whether it will still be relevant. In this post, I&#8217;m going to explain how .NET 5 improves code sharing and replaces .NET Standard. I&#8217;ll also cover the cases where you still need .NET Standard.<\/p>\n","protected":false},"author":335,"featured_media":29905,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[685,196],"tags":[9,30],"class_list":["post-29900","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-dotnet","category-dotnet-core","tag-net-core","tag-announcement"],"acf":[],"blog_post_summary":"<p>Since .NET 5 was announced, many of you have asked what this means for .NET Standard and whether it will still be relevant. In this post, I&#8217;m going to explain how .NET 5 improves code sharing and replaces .NET Standard. I&#8217;ll also cover the cases where you still need .NET Standard.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts\/29900","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/users\/335"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/comments?post=29900"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts\/29900\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/media\/29905"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/media?parent=29900"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/categories?post=29900"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/tags?post=29900"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}