Does C# 8 support the .NET Framework?
Asked Answered
E

3

251

In Visual Studio 2019 Advanced Build settings, C# 8 does not appear to be available for a .NET Framework project, only (as in the picture below) for a .NET Core 3.0 project:

enter image description here

Does C# 8 support the .NET Framework?

Edmond answered 18/6, 2019 at 14:31 Comment(0)
P
468

Yes, C# 8 can be used with the .NET Framework and other targets older than .NET Core 3.0/.NET Standard 2.1 in Visual Studio 2019 (or older versions of Visual Studio if you install a NuGet package).

The only thing required is to set language version to 8.0 in the csproj file. You can also do this in Directory.Build.props to apply it to all projects in your solution. Read below for how to do this in Visual Studio 2019, version 16.3 and newer.

Most - but not all - features are available whichever framework is targeted.


Features that work

The following features are syntax changes only; they work regardless of framework:

Features that can be made to work

These require new types which are not in the .NET Framework. They can only be used in conjunction with "polyfill" NuGet packages or code files:

Default interface members - do not, cannot, and never will work

Default interface members won't compile under .NET Framework and will never work because they require runtime changes in the CLR. The .NET CLR is now frozen as .NET Core is now the way forward.

For more information on what does and doesn't work, and on possible polyfills, see Stuart Lang's article, C# 8.0 and .NET Standard 2.0 - Doing Unsupported Things.


Code

The following C# project targetting .NET Framework 4.8 and using C# 8 nullable reference types compiles in Visual Studio 16.2.0. I created it by choosing the .NET Standard Class Library template and then editing it to target .NET Framework instead:

.csproj:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFrameworks>net48</TargetFrameworks>
    <LangVersion>8.0</LangVersion>
    <Nullable>enable</Nullable>
  </PropertyGroup>
</Project>

.cs:

namespace ClassLibrary1
{
    public class Class1
    {
        public string? NullableString { get; set; }
    }
}

I then tried a .NET Framework 4.5.2 WinForms project, using a legacy .csproj format, and added the same nullable reference type property. I changed the language type in the Visual Studio Advanced Build settings dialog (disabled in 16.3) to latest and saved the project. Of course as this point it doesn't build. I opened the project file in a text editor and changed latest to preview in the build configuration PropertyGroup:

<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
   <LangVersion>preview</LangVersion>

I then enabled support for nullable reference types by adding <Nullable>enable</Nullable> to the main PropertyGroup:

<PropertyGroup>
   <Nullable>enable</Nullable>

I reloaded the project, and it builds.


Visual Studio 2019

There has been a major change in the RTM version of Visual Studio 2019 version 16.3, the launch version for C# 8.0: the language selection dropdown has been disabled:

enter image description here

Microsoft's rationale for this is:

Moving forward, ... each version of each framework will have a single supported and default version, and we won't support arbitrary versions. To reflect this change in support, this commit permanently disables the language version combo box and adds a link to a document explaining the change.

The document which opens is C# language versioning. This lists C# 8.0 as the default language for .NET Core 3.x ONLY. It also confirms that each version of each framework will, going forward, have a single supported and default version and that the framework-agnosticism of the language can no longer be relied on.

The language version can still be forced to 8 for .NET Framework projects by editing the .csproj file.


The gory details

When this answer was first written, C# 8 was in preview and a lot of detective work was involved. I leave that information here for posterity. Feel free to skip it if you don't need to know all the gory details.

The C# language has historically been mostly framework neutral - i.e. able to compile older versions of the Framework - although some features have required new types or CLR support.

Most C# enthusiasts will have read the blog entry Building C# 8.0 by Mads Torgersen, which explains that certain features of C# 8 have platform dependencies:

Async streams, indexers and ranges all rely on new framework types that will be part of .NET Standard 2.1... .NET Core 3.0 as well as Xamarin, Unity and Mono will all implement .NET Standard 2.1, but .NET Framework 4.8 will not. This means that the types required to use these features won’t be available on .NET Framework 4.8.

This looks a bit like Value Tuples which were introduced in C# 7. That feature required new types - the ValueTuple structures - which were not available in NET Framework versions below 4.7 or .NET Standard older than 2.0. However, C# 7 could still be used in older versions of .NET, either without value tuples or with them by installing the System.ValueTuple Nuget package. Visual Studio understood this, and all was fine with the world.

However, Mads also wrote:

For this reason, using C# 8.0 is only supported on platforms that implement .NET Standard 2.1.

...which if true would have ruled out using C# 8 with any version of the .NET Framework, and indeed even in .NET Standard 2.0 libraries which only recently we were encouraged to use as a baseline target for library code. You wouldn't even be able to use it with .NET Core versions older than 3.0 as they too only support .NET Standard 2.0.

The investigation was on! -

  • Jon Skeet has an alpha version of Noda-Time using C# 8 ready to go which targets .NET Standard 2.0 only. He is clearly expecting C# 8/.NET Standard 2.0 to support all frameworks in the .NET family. (See also Jon's blog post "First steps with nullable reference types").

  • Microsoft employees have been discussing the Visual Studio UI for C# 8 nullable reference types on GitHub, and it is stated that they intend to support the legacy csproj (pre-.NET Core SDK format csproj). This is a very strong indication that C# 8 will be usable with the .NET Framework. [I suspect they will backtrack on this now that the Visual Studio 2019 language version dropdown has been disabled and .NET has been tied to C# 7.3]

  • Shortly after the famous blog post, a GitHub thread discussed cross-platform support. An important point which emerged was that .NET Standard 2.1 will include a marker that denotes that default implementations of interfaces is supported - the feature requires a CLR change that will never be available to the .NET Framework. Here's the important bit, from Immo Landwerth, Program Manager on the .NET team at Microsoft:

Compilers (such as C#) are expected to use the presence of this field to decide whether or not to allow default interface implementations. If the field is present, the runtime is expected to be able to load & execute the resulting code.

  • This all pointed to "C# 8.0 is only supported on platforms that implement .NET Standard 2.1" being an oversimplification, and that C# 8 will support the .NET Framework but, as there is so much uncertainty, I asked on GitHub and HaloFour answered:

IIRC, the only feature that definitely won't appear on .NET Framework is DIM (default interface methods) as that requires runtime changes. The other features are driven by the shape of classes that might never be added to the .NET Framework but can be polyfilled through your own code or NuGet (ranges, indexes, async iterators, async disposal).

C# 8 will be fully supported on .net core 3.0 and .net standard 2.1 only. If you manually edit the project file to use C# 8 with .net core 2.1, you are in unsupported territory. Some C# 8 features will happen to work well, some C# 8 features will work not too well (e.g. poor performance), some C# 8 features will work with extra hacks, and some C# 8 features will not work at all. Very complex to explain. We do not actively block it so the expert users who can navigate through it can do so. I would not recommend this unsupported mix&match to be used broadly.

(Jan Kotas)

People like you who are willing understand -- and work around them -- are free to use C# 8. The point is, not all language features will work on down-level targets.

(Immo Landwerth)


Caveat emptor

The C# 8/.NET Framework combination is not officially supported by Microsoft. It is, they say, for experts only.

Prowl answered 13/7, 2019 at 16:6 Comment(17)
This should clear any confusion stemming from the fact we can, if we try, use some C# 8 features outside of Standard 2.1 - github.com/dotnet/corefx/issues/40039Domesticate
The new nullable attributes (learn.microsoft.com/en-us/dotnet/csharp/nullable-attributes) required to design the more complex nullable use cases are only available in System.Runtime.dll that ships with .NET Core 3.0 and .NET Standard 2.1. This makes nullable \ C# 8.0 incompatible with ,NET Framework 4.8Clancy
@BenHall I've added some takeaways from your issue - thank you very much for raising the issue and for posting here. Please feel free to edit the answer if it is any way incorrect.Prowl
@VictorDerks That's great info, thank you. I've quoted you directly in the answer. Please feel free to edit the answer if it is any way incorrect.Prowl
Nullable attributes can be used in your own projects (and the compiler will respect them), by including the source at github.com/dotnet/coreclr/blob/master/src/… . It's probably a good idea to either set the compiler constant INTERNAL_NULLABLE_ATTRIBUTES in your project so the accessibility modifier on the attributes will be internal; or to change the modifiers by hand.Fluorinate
Also, Reference Assembly Annotator adds nullable attributes to the .NET Framework APIs in the places where they've been added in the .NET Core 3.0 API (such as Dictionary.TryGetValue).Fluorinate
There's a source-only NuGet package which adds the nullable attributes to your project, for older .NET versions.Fluorinate
Visual Studio 2019 IntelliSense doesn't support nullable reference types when specified through <Nullable>enable</Nullable> in the csproj. It seems to work when using #nullable enable directives. See also: github.com/dotnet/project-system/issues/5551Plasmodium
@Plasmodium Are you using an old-style .csproj, or an SDK format csproj? The referenced issue refers to the old-style project format.Fluorinate
@ZevSpitz that's correct; <Nullable> is a configuration element in csproj (as shown in the answer I'm commenting on).Plasmodium
So, I suppose we could say it is "dangerous" to use latest / 8.0 version of C# in a project targeting .NET Standard 2.0. It could be consumed by a Netfx app that won't support all the subtleties of its dependency and yet nothing prevents one to do so. Am I correct?Guardafui
@Guardafui I would have no qualms targetting C# 8 and using the basic features which don't require polyfills (already do that), and possibly with the polyfills too (haven't needed them). However, the best advice I can be is: if in doubt, don't do it, at least not if your job depends on it.Prowl
Is there any library to enable Indices and ranges for .NET Framework projects?Pennywise
solution #62648689Valve
A hacky way of adding default interface methods would be to add extension methods to the interface. (learn.microsoft.com/en-us/dotnet/csharp/whats-new/tutorials/…) Probably not the best idea, but default interface methods are already controversial.Modestia
Then it would be great if Visual Studio 2019 wasn't always nagging me to refactor my .NET Framework project's code to C# 8.0 features and marked these as errors if used.Collinsworth
A very useful polyfill package is PolySharpDoth
N
35

According to this blog entry the language is indeed tied to the framework:

This means that the types required to use these features won’t be available on .NET Framework 4.8. Likewise, default interface member implementations rely on new runtime enhancements, and we will not make those in the .NET Runtime 4.8 either.

For this reason, using C# 8.0 is only supported on platforms that implement .NET Standard 2.1. The need to keep the runtime stable has prevented us from implementing new language features in it for more than a decade. With the side-by-side and open-source nature of the modern runtimes, we feel that we can responsibly evolve them again, and do language design with that in mind. Scott explained in his Update on .NET Core 3.0 and .NET Framework 4.8 that .NET Framework is going to see less innovation in the future, instead focusing on stability and reliability. Given that, we think it is better for it to miss out on some language features than for nobody to get them.

Neall answered 18/6, 2019 at 14:39 Comment(1)
Many more details in the other answer by Stephen Kennedy. In fact, it is easy enough to make a substantial subset of C# 8.0 work when targeting .NET Framework. But some parts of C# 8.0 require changes to the runtime which Microsoft is not going to make for the "old" .NET Framework. And they seem to be tying the language version and the .NET version more closely together.Leathery
B
2

Here are the listed new features of C# 8 and whether they require runtime changes or are simply compiler changes:

  • Readonly members (compiler)
  • Default interface methods (runtime)
  • pattern matching enhancements (compiler)
    • switch expressions
    • Property pattern
    • Tuple patterns
    • Positional patterns
  • Using declarations (compiler)
  • Static local functions (compiler)
  • Disposable ref structs (compiler)
  • Nullable reference types (compiler with a caveat, nullable analysis partly relies upon types that won't exist)
  • Asynchronous streams (compiler, need to install Microsoft.Bcl.AsyncInterfaces)
  • Indices and ranges (compiler, but needs class system.Range plus other classes)
  • Null-coalescing assignment (compiler)
  • Unmanaged constructed types (compiler)
  • Stackalloc in nested expressions (compiler)
  • Enhancement of interpolated verbatim string (compiler, @$ is the same as $@)
Buckra answered 18/8, 2023 at 13:42 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.