Testing C# 9.0 in VS2019 - CS0518 IsExternalInit is not defined or imported ... How do I define/import it?

Viewed 12974

EDIT [Nov 29 2020]: .NET 5.0 is out now, but the solution below is still required if you're targetting .NET Standard 2.1


C# 9.0 is still under development. There are a couple references which lead me to believe it should be testable now (some of it, anyway).

  1. A Microsoft blog by Mr. Awesome himself, introducing the features. https://devblogs.microsoft.com/dotnet/welcome-to-c-9-0/
  2. The language tracking page on github: https://github.com/dotnet/roslyn/blob/master/docs/Language%20Feature%20Status.md

I'm using VS 2019 16.7 Preview 3.1. I've selected the language version as Preview for a project.

Some C# 9 features, I can use. Like: Dictionary<string, object> Stuff = new()

But using the new init feature gives me this error: Error CS0518 Predefined type 'System.Runtime.CompilerServices.IsExternalInit' is not defined or imported

How do I fix this?

Examples of code causing the error:

class Test
{
   public int Hello { get; init; }
}

and

record Test(int hello);

The record definition is shorthand and expands into something that uses init, which is why it's also affected.

The language tracking page I linked to above says the feature was Merged into 16.7p3, which I am using.

Am I just being overly excited? Do I need to wait? Or is there a way to play with these features right now :D

EDIT (requested in comments) - Adding csproj for .net 5.0 console app:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net5.0</TargetFramework>
    <LangVersion>preview</LangVersion>
  </PropertyGroup>
</Project>

EDIT #2: A workaround posted here - https://github.com/dotnet/roslyn/issues/45510

4 Answers

This is a bug in the current preview and the latest master branch (June 27). A simple record in sharplab.io creates the same error.

Just add the missing type somewhere in your project

using System.ComponentModel;

namespace System.Runtime.CompilerServices
{
    [EditorBrowsable(EditorBrowsableState.Never)]
    internal class IsExternalInit{}
}

Records and init will work without problem.

Only LinqPad 6 seems to work without problems, probably because it includes that type too

Borrowing from Panagiotis Kanavos, you can actually declare IsExternalInit as a record itself (self fulfilling prophecy):

using System.ComponentModel;

namespace System.Runtime.CompilerServices
{
    [EditorBrowsable(EditorBrowsableState.Never)]
    public record IsExternalInit;
}

If you don't like to litter each and every project with the IsExternalInit class, you can use Directory.Build.props to automatically reference the file as an invisible compile item:

<?xml version="1.0" encoding="utf-8"?>
<Project>
    <ItemGroup>
        <Compile Include="$(MSBuildThisFileDirectory)IsExternalInit.cs" Visible="false" />
    </ItemGroup>
</Project>

Add/edit this file in your solution's root and add the IsExternalInit.cs file there as well. Doing so will add that file to all your projects automatically.

Example folder structure relative to your solution:

.
├── Directory.Build.props
├── IsExternalInit.cs
└── Solution.sln

Nice answer of @Brouke, I'll juste explain more :

1 - In your root folder (solution folder), create a file called Directory.Build.props where *.pros is its extension.

2 - Edit this file and add :

<?xml version="1.0" encoding="utf-8"?>
<Project>
    <ItemGroup>
        <Compile Include="$(MSBuildThisFileDirectory)IsExternalInit.cs" Visible="false" />
    </ItemGroup>
</Project>

3 - Add the IsExternalInit.cs file alongside, with :

using System.ComponentModel;

namespace System.Runtime.CompilerServices
{
    [EditorBrowsable(EditorBrowsableState.Never)]
    public record IsExternalInit;
}

4 - Delete all other IsExternalInit.cs files, rebuild solution and restart VS.

Related