Why Doesn’t This New C# Feature Compile?

It seems so simple. Someone posts an example of how to do something using a cool, new feature in C# and you try to use their code and it fails to compile. Or you see something documented as working and you try it and it fails to compile. Why is that? It actually is quite simple but probably not at all obvious to the average developer.

One of the big changes in Visual Studio 2017 is that C# is now releasing incremental language updates with Visual Studio updates. We are currently at C# 7.3 but Visual Studio 2017 shipped with C# 7.0. The theory with this approach is that we can get access to new language features earlier. While this may be a good thing it does cause serious problems if everyone isn’t using the same language version. For example if I want to use a new language feature like generic constraints on enums then I need C# 7.3 which shipped in 15.7 (Visual Studio 2017 Update 7). Provided everybody on my team (and my build system) is using 15.7 or higher then it works. But if they haven’t it will fail to compile.

Unfortunately which version of C# is in which update to Visual Studio can be confusing. The documentation linked earlier defines the updates but who reads that anyway? Microsoft’s view of the world is that everyone should be updating as often as updates come out but that isn’t realistic in many situations such as lab environments and build systems. One of the questions that every team should ask is “which version of the language will we target?”
The answer really depends upon how often you want to update. If you trust Visual Studio updates and have no problems updating frequently (e.g. monthly) then the latest language version might be a good choice. However if your team does not like to update frequently or you have a build system that requires manual updates then quarterly or longer may be a better option. In that case targeting a specific version (e.g. 7.1 or 7.2) may be a better choice. This will help ensure that everyone on the team is using the same version. Once you’re team has decided on the Visual Studio update to use you now know the language version you can use.

OK, my team has all installed the correct update but the compiler is still complaining, now what? It is not obvious at all but simply installing the update doesn’t automagically update your projects to use the new features. This makes sense if you think about it because you don’t want a member on your team to update Visual Studio and suddenly use features that aren’t available to the rest of the team. This is where the project settings come in. Open up each of your C# project files and go to the Build tab. Then go to Advanced. In the advanced options you will see an option for compiler version. This is the magic switch and it is documented here.

By default projects will use the latest major version of the language, 7.0. If Visual Studio vNext is released with C# 8.0 then your projects will compile against that. But for the minor language releases your code will continue to target 7.0. So if you want to take advantage of a new language feature in your project you need to either update the project to target the specific version you want (e.g. 7.3) or change the option to use the latest minor version. Now your project will compile against the current version of the compiler. Remember though that if you use features in the minor version that aren’t available in previous versions and everyone hasn’t updated then they won’t be able to compile the code. So use the latest option only if all your team updates together.

It is a little unclear at this time what happens if you compile your code with newer language features and try to use that code on an older compiler version. Many of the language features are in the compiler and shouldn’t impact other code but there are some features that may not behave correctly. Enum generics come to mind here. If you’re going to support building code for varying compiler versions with newer features be sure to test your code against all the versions you’ll support.

Note, given the direction Microsoft has gone with TypeScript, MSTest and other frameworks I wouldn’t be surprised if at some point the compiler itself doesn’t become a NuGet package such that you download the version of the C# compiler your code will run against at build time. This would help resolve the update issue at the cost of the IDE having more to be concerned with.