How to use Nullable Reference Types in .NET Standard 2.0 and .NET Framework

 
 
  • Gérald Barré

This post is part of the series 'C# 8'. Be sure to check out the rest of the blog posts of the series!

In the previous post, I've explained how to use Nullable Reference Types and why this is a good feature. However, this feature works well only with .NET Core 3.0.

Note that C# 8.0 is not meant for older targets, such as .NET Core 2.x or .NET Framework 4.x. So some additional language features may not work unless you are targeting .NET Core 3.0 or .NET Standard 2.1

https://devblogs.microsoft.com/dotnet/try-out-nullable-reference-types/#turn-on-nullable-reference-types

It doesn't mean you cannot use it with .NET Core 2.x, .NET Framework 4.x or .NET Standard 2.0 but this is not the supported use-case. As .NET Standard 2.0 will be the preferred target for a long time when writing a library, I'll show you how to use Nullable Reference types with older frameworks.

#Multitarget .NET Core 3.0 to have BCL annotations

.NET Standard itself doesn't have any nullable annotations yet. If you're targeting .NET Standard, then you can use multi-targeting for .NET Standard and .NET Core 3.0, even if you don't need .NET Core specific APIs. The benefit is that the compiler will use the nullable annotations from CoreFX to help you get your annotations right. For instance, if you use string.IsNullOrEmpty, in .NET Core 3.0 the compiler understands that the value is not null when the result is false whereas there is no annotation in .NET Standard 2.0. To add .NET Core 3.0 as a target framework, open the csproj file, and add netcoreapp3.0 in TargetFrameworks:

csproj (MSBuild project file)
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFrameworks>netcoreapp3.0;netstandard2.0</TargetFrameworks> <!-- 👈 Include .NET Core 3.0 in the targets -->
    <LangVersion>8.0</LangVersion>
    <Nullable>enable</Nullable>
  </PropertyGroup>
</Project>

Note that to get the right IntelliSense, you need to select .NET Core 3.0 in Visual Studio:

For instance, you'll get a warning if you select .NET Standard 2.0:

But there is no warning when you select .NET Core 3.0:

#Remove warnings in older frameworks

Even if you select .NET Core 3.0 in the editor, you'll still see warnings in the error window for other targets. For instance, you can see that the warning applies only for "ClassLibrary1 (netstandard2.0)", everything's ok with .NET Core 3.0:

You can remove these warnings only for older frameworks by editing the csproj:

csproj (MSBuild project file)
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFrameworks>netcoreapp3.0;netstandard2.0</TargetFrameworks>
    <LangVersion>8.0</LangVersion>
    <Nullable>enable</Nullable>
  </PropertyGroup>

  <!-- 👇 disable the nullable warnings when compiling for .NET Standard 2.0 -->
  <PropertyGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">
    <NoWarn>$(NoWarn);nullable</NoWarn>
  </PropertyGroup>
</Project>

This way you still have warnings in .NET Core 3.0, but not when targeting .NET Standard 2.0.

#Add Nullable attributes in the project

.NET Core 3.0 adds new attributes to support the Nullable Reference Types features. You can check the previous post for more information. If you use them in .NET Standard 2.0, you'll have a compilation error.

The solution is to add them only in .NET Standard 2.0 so that it compiles. The nullable attributes are available in the CoreFX repository. I think it's better to define these attributes as internal and only for .NET Standard 2.0, so I've made a snippet that does that: NullableAttributes.zip.

C#
// Full code: https://www.meziantou.net/assets/nullableattributes.zip

// Original code: https://github.com/dotnet/runtime/blob/419e949d258ecee4c40a460fb09c66d974229623/src/libraries/System.Private.CoreLib/src/System/Diagnostics/CodeAnalysis/NullableAttributes.cs

#define INTERNAL_NULLABLE_ATTRIBUTES
#if NETSTANDARD2_0

// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

namespace System.Diagnostics.CodeAnalysis
{
    /// <summary>Specifies that null is allowed as an input even if the corresponding type disallows it.</summary>
    [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property, Inherited = false)]
#if INTERNAL_NULLABLE_ATTRIBUTES
    internal
#else
    public
#endif
        sealed class AllowNullAttribute : Attribute
    { }

    /// <summary>Specifies that null is disallowed as an input even if the corresponding type allows it.</summary>
    [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property, Inherited = false)]
#if INTERNAL_NULLABLE_ATTRIBUTES
    internal
#else
    public
#endif
        sealed class DisallowNullAttribute : Attribute
    { }

    /// <summary>Specifies that an output may be null even if the corresponding type disallows it.</summary>
    [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, Inherited = false)]
#if INTERNAL_NULLABLE_ATTRIBUTES
    internal
#else
    public
#endif
        sealed class MaybeNullAttribute : Attribute
    { }

    ...
}

You can download the file and add it to your project to use the attributes, so you can use the nullable attributes in .NET Standard 2.0 and .NET Core 3.0.

If you have multiple projects such as in my GitHub project Meziantou.Framework, you don't want to copy this file in every project. Instead, I choose to inject it using the Directory.Build.props file. Here's the file structure:

In the Directory.Build.props file, add the following code:

csproj (MSBuild project file)
<Project>

  <ItemGroup>
    <Compile Include="$(MSBuildThisFileDirectory)/Nullable.cs" />
  </ItemGroup>

  <!-- Ensure .NET Core 3.0 is a target of the project to support nullable reference types -->
  <Target Name="CheckNetCoreApp3_0" BeforeTargets="Build" Condition="$(TargetFrameworks.Contains('netcoreapp3.0')) == false">
    <Error Text="The project must target netcoreapp3.0" />
  </Target>

</Project>

#Conclusion

These three steps should allow you to support the Nullable Reference Types feature even in projects that target .NET Standard 2.0 or .NET Framework. The developer experience is ok as long as you don't forget to select .NET Core 3.0 in Visual Studio.

Do you have a question or a suggestion about this post? Contact me!

Follow me:
Enjoy this blog?Buy Me A Coffee💖 Sponsor on GitHub