49
votes

I've heard of

  • .Net Core
  • .Net Portable
  • .Net Standard
  • .Net Compact
  • Universal Windows Platform
  • Portable Class Libraries

All of these were explained to me as "a subset of the full .Net that allows you to target multiple platforms". So my questions are

  1. What's the difference!?
  2. If I want to write a library that's usable to as large an audience as possible, which one (or more than one) of these do I need to use?

(My specific situation: I have a library that targets .Net 2.0, .Net 4.5, and UWP. Targeting UWP required creating a new VS project and linking all the existing files, which is a huge pain. Now someone is telling me it doesn't work for PCL, and from the sound of it I have to do it AGAIN for .Net Standard!?)

3
@downvoter: Why the downvote? Was my question not clear? I've tried to research but have not found anything elucidating, only more confusion (eg. this image implies .Net Core Library is separate from .Net Base Class Library, but this image implies they're both shared)BlueRaja - Danny Pflughoeft
There's a detailed discussion herePeter Torr - MSFT
@PeterTorr-MSFT: I've read that, it's part of what led to this question. It doesn't answer why .Net Framework and .Net Core are considered different things, or how they differ. It doesn't answer what version(s) of .Net Framework/Core that .Net Standard is compatible with. It doesn't address how the other "subsets of the full framework" fit in, and doesn't tell me which framework(s) I need to target to support everyone.BlueRaja - Danny Pflughoeft
You forgot .NET Micro. I mean, not too many people care about it anymore, but as long as we're aiming for completeness and also mentioning Compact...Jeroen Mostert
Further confusing the issue is that almost no article seems to make a clear distinction between platform (UWP, Mono, CLR, .Net Core, .NET Native and various Phone/embedded flavors), build technology (MSBuild, .NET Core, .NET Native) and runtime library set (BCL, Silverlight, UWP, .NET Core, .NET Native). Not all combinations exist. .NET Standard is an attempt to standardize through the library, but deliberately leaves platform and build technology for you to untangle from the rest. This should become less confusing as .NET Core matures and eats the others, but that's a way off.Jeroen Mostert

3 Answers

51
votes

I'll answer your second question first:

I have a library that targets .Net 2.0, .Net 4.5, and UWP. Targeting UWP required creating a new VS project and linking all the existing files, which is a huge pain. Now someone is telling me it doesn't work for PCL, and from the sound of it I have to do it AGAIN for .Net Standard!?)

If I want to write a library that's usable to as large an audience as possible, which one (or more than one) of these do I need to use?

Short answer: you should target netstandard. Use the lowest version that has all the APIs you need. You can use a tool like API Port to check your existing project for compatibility with a given netstandard version.

Unfortunately, this approach will leave behind older platforms, in your case, .NET 2.0. If maintaining .NET 2.0 support is necessary, then you'll need a separate project (with linked files) to build a separate .NET 2.0 assembly.

On to the details...

What's the difference!?

  • .Net Standard (netstandard) - this is the new cross-platform BCL API. It's a "standard" in the sense that it's just an API definition and not an implementation. The idea is that you can compile your library to (a version of) this API and it will run on any platform that supports that version.
  • .Net Core - you can think of this as a reference implementation of netstandard (with a few extra bits). It is a cross-platform implementation of that API. It is possible that UIs and other frameworks may build on it, but for now its only sure foothold is acting as the platform of choice for ASP.NET Core. [Side note: for historical reasons, ".NET Core" is completely different than the netcore NuGet target; when you're in a NuGet context, netcore means "Windows 8/8.1/10"].
  • .Net Portable and Portable Class Libraries - Portable Class Libraries (PCLs) are a least-common-denominator approach to providing a cross-platform API. They cover a wide range of target platforms, but they are incomplete and not future-proof. They have essentially been replaced by netstandard.
  • .Net Compact - This is a completely different .NET framework with its own unique API. It is completely incompatible with any other framework, PCL, or netstandard version; as such, it is much more difficult to support than any other platform. However, it is still used on devices with tight memory constraints.
  • Universal Windows Platform - This was a Win10-era merging of the API between Windows Phone and desktop, allowing Windows Store apps/libraries to be written for both platforms. This has essentially been replaced by netstandard.
22
votes

As linked in the comments, there is already a description by Microsoft that outlines all of this information. However, by your response, it seems as though you didn't fully understand the entire thing. It's very long, so here's (hopefully) a tl;dr version.

We'll start with the following table from the above link, and hopefully this will clear up some of the confusion:

.NET Standard compliance

A brief overview for other pople who find this later: The .NET libraries have gone over a lot of changes and ports over its 15 years of existence. In that time, many smartphones are now nearly as powerful as some desktops that were in use back in 2001. In that intermin, subsets of the .NET framework were created (and quickly abandoned) for use in different platforms. With Satya Nadella's new approach to making .NET as wide-reaching of a platform as possible, things needed to change.

Being a 15 year old technology, things needed to be improved. .NET Core has been worked on since 2014 as a complete overhaul of the .NET architecture. It has been rewritten from the ground up as a new version of the .NET language. One of the goals of Core was to enable cross-platform deployment. Whether it's an app to run on an iPhone/Android/XBox One, or a website that can be hosted in IIS, or on a Linux box, .NET Core has you covered. It does this by several different ways, including not requiring the .NET Framework to be installed on the machine, and will instead package the necessary libraries with your solution.

Most notably with .NET Core is the drastic changes to ASP.NET. The old System.Web is completely gone and rewritten to be as performant as possible with impressive results. The separate WebApi controllers are gone, as everything is done within a single controller. The entire process is now opt-in as opposed to defaulting to allow things you might not want to.

However, we as developers will want to migrate some applications eventually, so how can we be sure that the code we've already written hasn't had several little minor name changes to methods thus breaking the compilation of giant solutions? In comes the .NET Standard. This is a set of APIs that must be implemented in order for your platform to call itself ".NET".

As the basic .NET Framework we've been working with for years is well-established, it was used as the basis for what the Standard will encompass. However, everything isn't included, as what would be the point? So the Standard is only what common APIs will exist between the various types of the .NET platforms. You will not eventually be writing code in ".NET Standard".

Xamarin (included in the above table) was purchased by Microsoft in 2016, and that technology was used to help build (or at least inspire) .NET Core to be cross-platform. It still exists as a tool, but in the same veins as it was used in the past. According to the table, it will be .NET Standard 2.0 compliant in the vNext release. However, it's target audience will not change.

To directly answer your question, if you want to write an application with the widest possible, single deploy solution, you would want to use .NET Core. However, if you're using a library that is currently built upon .NET Framework 2.0 and 4.5, then you will be stuck using .NET Framework and having that separate UWP solution for that target.

If it provides something that you can call through a Web API call, you could have that running on your server in .NET Framework, and have a single solution in .NET Core to deploy to your end users. If it is integrated to your code, you're unfortunately out of luck until a .NET Core update is provided.

Hopefully this has cleared up some of the confusion among the different technology names.

EDIT

After some clarification on your specific situation, I can clear things up for you. You can not make a single solution that will target both .NET Framework and .NET Core. The compile in completely different ways, with different underlying technology, so this is the same situation as trying to use your .NET 4.5 version in a .NET 2.0 solution.

However, there are tutorials out there to allow for you port your project to Core. For the most part, just copy the class bodies into your .NET Core solution, and most stuff will work correctly. There are some pieces that have been abandoned, some that haven't been completely 100% fleshed out yet (not for your case, but Entity Framework doesn't have all the same features for example). There are also some calls that have changed a little bit.

The good new is that moving forward, .NET Core will give you the broadest reach possible. .NET Framework is not going away, but it and Core will be much more in sync with each other.

The other benefit to .NET Core is that it uses an iterative approach to deployment so you won't be waiting 2 years for the next major upgrade. With everything being delivered through NuGet, you'll have a much faster turn-around on improvements and new features.

21
votes

I'm assuming you've already read the Microsoft article with the nice table and everything is clear as mud now. If so, you're in the same boat as I was before I spent the better part of an afternoon looking into this (and trying to port my reflection-heavy library to .NET Core, which I should mention is also my only .NET Core porting effort). What follows is not the official party line but my personal summary of what I found through lots of reading (and from being a .NET developer since .NET 1.0). I cannot vouch for its total accuracy (especially when it comes to mobile development, of which I'm almost entirely ignorant) and corrections are certainly welcome. If there are lots I'll just make it wiki.

I will walk through it more or less chronologically because I've found that just makes the most sense if you want to understand how the old and new relate. It should probably be pared down a lot, but at present I lack the time to do so. There is a TL;DR version at the very end, though.

The long and winding road

Despite its Java ancestry, .NET has never seriously attempted to be "write once, run anywhere". It started off as very much in the camp of Windows, and even though it compiles to bytecode and did not go overboard with explicit Windowsisms and is thus theoretically very portable, that's not what MS was really interested in. Part of the .NET Framework was open sourced early on, and a bunch of open source enthousiasts picked it up and ran with it, giving us Mono. Mono is important because it's the first alternate platform and library set for .NET and illustrates the ideas of platform versus library versus toolchain. Mono attempts to give a (more or less) complete implementation of the Common Language Runtime and it's associated Base Class Library. This is important: although Mono runs on Linux (and some other Unices) it is not a separate plaform in that it implements (some versions of) the CLR + BCL. There are runtime differences (path names and the like) that matter to application developers, but for practical library programming, you can consider Mono and the .NET Framework for Windows "the same" platform with a slightly different implementation. I stress this because we're going to encounter .NET code targeting Windows that is not running on the CLR and which is (ironically or otherwise) harder to port for.

Then came along Windows Phone (multiple versions), Windows CE (multiple versions), Windows Embedded CE, Windows Toaster (OK, that one doesn't really exist) and some flavor of .NET was basically reinvented every time -- still .NET but with fundamental stuff from the runtime and/or BCL missing or changed. Here's where we get .NET Compact, .NET Micro, Windows Phone (old style, no separate name for the framework) and Silverlight. All of these should be considered separate platform + library combos that resemble the .NET Framework enough to make cross-platform development possible, but differ from it enough to make it not that easy. As keeping track of what was supported where became most inconvenient for shared libraries, someone came up with the idea of Portable Class Libraries and their associated profiles (the collection of which is known as the .NET Portable Reference Assemblies).

Basically, you target a set of specific combinations of .NET version, platform (and library), and you'd get reference assemblies that mimicked those combinations to compile against. Many different profiles exist depending on what flavors your explicitly wish to target. This was a first attempt at what .NET Standard is now trying on a grander scale. Basically, PCL is now obsolete unless you're targeting something .NET Standard isn't trying to support. .NET Standard does away with the idea of a bazillion different profiles, which is good, at the expense of cutting some stuff your library could target earlier, which is bad. There are resources online to help with the transition from PCL to .NET Standard. If you're looking at portable code now, you don't want to focus on PCL unless you really want to support some pretty marginal platforms (no offense intended to people still developing for them).

Universal Windows Platform is, as the name implies, a platform. Specifically, it is the .NET Platform that's supported by Windows Store Apps (both on the desktop and on the phone). That's it, no more, no less. It's best considered as the natural successor to Silverlight in that it's a sandboxed framework support for both desktop and mobile. Despite the name, it is not a universal platform and it is not what you want all your code to target. It is a platform you may want to enable for your code, and it is unique in that it is the only platform I know of that has two runtimes in the same version. Coming right up!

.NET Native was not mentioned in the original post but often comes up in these discussions because it, too, is new, like .NET Core, and sounds very sexy because it compiles .NET directly to machine code (ahead of time compilation, not JIT compilation). It is not a complete new platform but a new runtime for UWP apps (and only those) when compiled in Release mode. In Debug mode, they use the CoreCLR (the .NET Core runtime). You won't need to think about this very hard unless you really want to build a UWP app, because there's all sorts of interesting stuff going on with reflection in .NET Native that needs separate attention from the app developer.

And now we come to .NET Core! .NET Core started off as "ASP.NET Core", but people quickly realized it could be much bigger than that. .NET Core is a new runtime (CoreCLR) + library combo with explicit cross-platform support (as in cross-OS). Unlike the CLR + BCL combo, where there's a Windows version and a Unix version in the form of Mono, .NET Core is one codebase for all platforms (with the usual platform specific crunchy bits to support the fluffy portable layer above, of course). What further confuses people is that .NET Core is also the name of a new toolchain/project type to support building .NET Core applications, where before we only had MSBuild. This was necessitated by there being no Visual Studio for Linux, but MS is already moving away from this "let's keep it simple and JSON" approach and moving back to one universal format for both .NET Framework and .NET Core (and it's going to be MSBuild, because there's much more resting on that).

.NET Core is for the most part very much compatible with the .NET Framework. As a result, if your .NET Core application is actually running on the .NET Framework (on Windows), it can load assemblies that target the .NET Framework, not merely .NET Core. This is an important source of confusion and unportable code: while you can build against and load those assemblies, they will make your code non-portable. .NET Core itself won't stop you from doing this; .NET Standard (coming right up) will, if you line up your declarations properly.

With me so far? Good, because now we're ready to drop .NET Standard on your unsuspecting head. .NET Standard is not a platform (in the sense that you can't download and install it on a machine), it is not a library (but there are packages to support it for build purposes), it is a profile. It's an attempt to standardize the library surface across a wide range of the things we've just discussed. The idea being, if your code targets .NET Standard X.Y, you only need to switch your build tools to "please give me .NET Standard X.Y" and when you build your assembly, you can be sure it will be usable for all the platforms covered by X.Y. Hooray! The world is simple again!

Well, not yet it isn't. The problem is that .NET Core is in heavy development at the moment, meaning a lot of stuff is missing or different -- even quite fundamental stuff that may be naturally present in your .NET Framework codebase, like marking your exceptions Serializable and giving them a separate constructor for deserialization so they work nicely across AppDomains. There are no AppDomains in .NET Core, hence no serialization, hence those constructors will not compile. Even the Serializable attribute itself is missing in older versions of CoreCLR. If your MSBuild project uses custom targets, too bad, the .NET Core toolchain has no support for that stuff at the moment (it may, in the future, when it's MSBuild again). You can rewrite, of course, but not reuse. So while you can target .NET Standard, you may need two separate projects and/or some conditional compilation to get your .NET Standard assembly for two different platforms. If you're lucky (or can compromise a bit), your library is simple enough that building it with .NET Core alone might suffice. Make no mistake, though: there are still multiple .NET platforms and they still have their differences, .NET Standard merely tries to make it easier to port. So far it's limited, but already doing a cleaner job than PCL.

To sum up a bit: .NET Core and .NET Framework (and all their little cousins and nephews) are separate platforms both conceptually and by implementation. .NET Standard is a targeting profile that simplifies efforts needed to port code between them (but does not yet make it completely transparent). PCL is the precursor to Standard that can be disregarded if you're progressive.


TL;DR starts here (but is still TL)

To finally answer your question, what should you do if you're a modern library developer and you want to target "as large an audience as possible"? Well first of all, you must make this smaller if possible. What platforms will you explicitly support and test against? Do you really want to target .NET Compact on your XBox 360? Windows Phone 7? The Silverlight of eight years ago? If you do, you probably can't get around the PCL, but most of us will have the luxury of being able to avoid that. If not: queue up a separate PCL build if your profile doesn't match anything in the .NET Standard.

Is your library very simple (using no reflection, no async/await, no dependencies on bigger frameworks like WCF)? Then you may be able to target the cross section of .NET 2.0 and the lowest version of .NET Standard that has the framework dependencies you need. Don't be fooled, the lower versions of .NET Standard are really disappointingly limited in what they offer, but you have to pay some price for portability. There is no toolchain support for building both for .NET 2.0 and some .NET Standard version. You must build it twice and test it "everywhere", although the cross-section means you can be reasonably sure it will work if it compiles. The resulting library will support every single .NET platform that can load vanilla .NET 2.0 assemblies (which is nearly all of them, but notably not Micro and Compact) and .NET Core, and all without platform toggles. Congrats, the world has never seen something so portable!

Will your library use reflection? Then you probably can't get around rewriting the code to make it compile for .NET Core, because the reflection API changed a while back and your code may not have caught up yet (as there was no need if you kept targeting the full framework only). You will want to bump your targeted .NET Framework version to 4.5 in this case, as this is the .NET Framework version that is compatible with these changes. Here you start getting tooling support: you can target .NET Standard 1.1, which covers a subset of .NET 4.5. If you find this subset is not enough, you will again have to resort to building twice: for the full .NET Framework and for .NET Core. The reason being that .NET Core 1.0 supports "more" than .NET Framework 4.5, but there is as yet no version of the .NET Framework you can target that's on par with Core (that will be "vNext"). So if you don't want to restrict yourself to .NET Core only but also want to support those of us still building plain old 4.5 desktop apps and .NET Standard 1.1 isn't enough for you, you'll have to split. The wrong thing to do is target 1.1 and then import Framework 4.5 only packages/assemblies, as this will get you the worst of both worlds in terms of portability!

Will your library need some of the improvements/extensions over 4.5 that were introduced in 4.5.1 or later, or packages that are only available for higher .NET Standard versions? Then target the appropriate higher .NET Standard version instead. Note that Microsoft no longer officially supports any 4.x lower than 4.5.2. This does not mean you shouldn't target those versions (go as low as you reasonably can), but it does mean that you have an argument for using nothing less than .NET Standard 1.2, and if you can demand 4.6, no less than 1.5. This is not burdensome on consumers (if you're willing and able to install 4.6, you're almost certainly willing and able to install 4.6.2) and makes your life easier. Depending on your code you could get away with only a .NET Core build, but you probably don't want to, because the .NET Core build chain isn't stable yet and will move back to MSBuild (as mentioned before). No sense in ditching all your project files for JSON only to move back again later!

Does your library use any sort of conditional compilation? Beware, with the .NET Core toolchain, you get different predefined symbols. They're extra super annoying because they insist on (say) making a distinction between 4.5, 4.5.1 and 4.5.2, which is a pain if you want to cover "4.5 and beyond". Nothing a careful build can't handle, but nevertheless something you need to take into account.

I'm not covering mobile builds here (Xamarin and older Phone versions) because, well, I know very little about those! I imagine the story is much the same as building for both .NET Core and the .NET Framework, in that building once only works for simple libraries and libraries where you don't have to care about backwards compatibility, and needs (at least) two builds otherwise, but as I said way back in the beginning, corrections are welcome.