Supporting .NET Standard and .NET 3.5
Like many .NET developers I’ve been keeping an eye on .NET Standard, but so far haven’t had much cause to use it for my own projects. My NAudio open source library is heavily dependent on lots of Windows desktop APIs, so there isn’t much incentive to port it to .NET Standard. However, another of my open source audio projects, NLayer, a fully managed MP3 decoder, is an ideal candidate. If I could create a .NET standard version of it, it would allow it to be used in .NET Core, UWP, Xamarin and Mono platforms.
The first step was to move to VS 2017 and replace the NLayer csproj
file with one that would build as a .NET Standard package. The new csproj
file format is delightfully simple, as it no longer requires us to specify each source file individually. I went for .NET Standard 1.3 and told it to auto-create NuGet packages for me, another nice capability of VS 2017:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard1.3</TargetFramework>
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
</PropertyGroup>
</Project>
Then I attempted to compile. There were a few minor errors to be fixed. Thread.Sleep
wasn’t available – I switched to Task.Delay
instead. And Stream.Close
needed to be replaced with Stream.Dispose
. But those changes aside, it was relatively painless.
Next I wanted to use the new .NET Standard version of NLayer within my NLayer.NAudioSupport project. This project references both NLayer and NAudio, and is itself a .NET 3.5 library. Unfortunately, when I tried to build I was told that a .NET 3.5 project cannot reference a .NET Standard 1.3 library. Now because NAudio is .NET 3.5, it wasn’t an option to convert NLayer.NAudioSupport to .NET Standard, so I needed another solution.
I consulted the compatibility matrix which made it clear that I needed to be on at least .NET 4.6 to be able to reference a .NET Standard 1.3 project. So I changed NLayer.NAudioSupport to target .NET 4.6 and sure enough, everything compiled and worked.
However, it seemed a shame that now I was forcing a major .NET version upgrade on all users of NLayer.NAudioSupport. NAudio is used a lot by companies who lag long way behind the latest versions of .NET. So is there any way to keep support for .NET 3.5 for those who want it, in addition to supporting .NET Standard 1.3?
Well, we can multi-target .NET frameworks. This is very easily done in the new csproj
syntax. Instead of a TargetFramework
node, we use a TargetFrameworks
node, with a semi-colon separated list of frameworks. So I just added net35
.
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netstandard1.3;net35</TargetFrameworks>
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
</PropertyGroup>
</Project>
Now when we build, it creates two assemblies – a .NET Standard library and a .NET 3.5 one. And the auto-generated NuGet package contains them both. But what happens if the same code won’t compile for both targets? In our case, for the .NET 3.5 build we needed to revert back to Thread.Sleep
again.
We can do this by taking advantage of conditional compilation symbols, which will be NETSTANDARD1_3
or NET35
in our instance. This allows me to use the API available on the target platform:
#if NET35
System.Threading.Thread.Sleep(500);
#else
System.Threading.Tasks.Task.Delay(500).Wait();
#endif
And with that, I now have a NuGet package containing versions of NLayer that can be used on a huge range of .NET platforms. If you’re the maintainer of an open source library, and you’ve been ignoring .NET Standard so far, maybe its time for another look.