Skip to content
This repository has been archived by the owner on Jun 30, 2023. It is now read-only.

Understanding multi targetting and NetStandard

Bogdan Gavril edited this page Nov 26, 2018 · 14 revisions

Multitargetting and NetStandard

At a glance

What is NetStandard

Consider the following programming metaphor - NetStandard is an interface and platforms such as net45, monoandroid8.0, netcoreapp are implementations of this interface. This means that the platform (e.g. net45) can have more public APIs than netstandard.

Our libraries are built against multiple frameworks, at the time of writing, these are:

  • netstandard1.3;
  • net45;
  • uap10.0;
  • MonoAndroid8.1;
  • Xamarin.iOS10;
  • netcoreapp1.0

How does .net resolve these platforms

When we build the package, msbuild runs a foreach loop on the platforms above. It builds a distinct DLL for each of them. If you have a look at a nupkg (just rename it to zip and look inside the archive), you'll see this.

It's actually NuGet that figures out which DLL is needed for a specific application. You can use this tool to simulate NuGet.

A few examples:

The compatiblity matrix, i.e. which platform implements which netstandard is outlined here. Consider that MSAL library is built against the frameworks described above and I take a dependency on MSAL from a library built against....

Before you see the results, try to fill this on your own...
Library that depends on MSAL NuGet uses MSAL built for...
net45
net46
netstandard1.1
netstandard2.0
netcore2.0
Xamarin.iOS 10
Xamarin.Mac 3.0
PCL
Windows 8.0
Solved Examples
Library that depends on MSAL NuGet uses MSAL built for...
net45 net45
net46 net45
netstandard1.1 Error
netstandard2.0 netstandard1.3
netcore2.0 netcore1.0
Xamarin.iOS 10 Xamarin.iOS 10
Xamarin.Mac 3.0 netstandard1.3
PCL Error
Windows 8.0 Error

Note that all of the platforms are backwards compatible, like concentric circles, e.g,:

netstandard1.3 extends netstandard1.2 extends netstandard1.1 etc. net461 extends net46 extends net45 etc.

What does it mean to build against NetStandard

You might be wondering why would anyone build an app / library against netstandard instead of net45, netcore etc? There seem to be 2 scenarios for this:

  1. Libraries like Newtonsoft.Json want to reach as many developers as possible, so they are built against netstandard1.0, which means they are compatible with everything
  2. Xamarin.XForm apps propose a code sharing scheme with a common DLL built against netstandard and entry-point DLLs built against iOS, UWP and Android. So for example I can deserialize some json in the common DLL and store in a platform specific store in UWP, iOS and Android.

What happens to NetStandard at runtime

Since netstandard is an interface, it cannot function on its own, i.e. all the methods in all the classes in netstandard.dll do nothing or throw exceptions. When an application, for example net45, takes a dependency on a netstandard library, the developer codes against these empty / exception throwing methods. But at runtime, these methods are typeforwarded to an actual implementation.

For example you might write Environment.GetEnvironmentVariable() in your netstandard library, and this will be typeforwarded to the actual implementation of getting env variables in .netcore, iOS, android, uwp etc.

Sharing code through netstandard is not trivial

When we develop multi-framework code, like ADAL and MSAL, we have 2 mechanisms of writing platform specific code:

  1. We use precompilation flags, e.g. #if ANDROID #if iOS etc. Similarly we exclude entire files / folders based on platform in the csproj file
  2. We define common interfaces like IWebUi and provide platform specific implementations

How do XForm apps work?

The structure is similar to the diagram below - a common.dll has all the common code and actual libraries have the platform specific code:

|Common.DLL (netstandard2.0)| ---> |MSAL|

|UWP.DLL|, |iOS.DLL|, |Android.DLL| --> |Common.DLL|

And here is the catch:

At build time, Visual Studio will tell you that you can use any API from the netstandard1.3 MSAL. This is because Common.dll is built against netstandard2.0 and MSAL is built against a bunch of stuff including netstandard1.3.

At run time, .NET will actually use the UWP (or iOS or Android) MSAL library, depending on what application you run.

As such, if in MSAL you define a piece of public API as:

#if NETSTANDARD1_3
public void Foo() {...}
#endif

You'll be able to take a dependency on Foo from the XForms app, but at runtime you'll get a method not implemented exception.

Prefer throwing PlatformNotSupported to #if

So a safer way to write code in libraries like MSAL is:

public void Foo() 
{
#if NETSTANDARD1_3
    throw new PlatformNotSupportedException();
#endif
#elif WINDOWS_APP
    // uwp implementation or exception
#else
    // other implementations ...
#endif
}

This way the public entry point is available in XForms at build time and will be replaced by the proper implementation at runtime.

Clone this wiki locally