70

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

Josh
  • 2,958
  • 3
  • 16
  • 27

4 Answers4

99

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

Panagiotis Kanavos
  • 120,703
  • 13
  • 188
  • 236
  • strange - the issue is actually worse in preview 7 for me. Before, I just had to define the above code in one of my class libraries, and anything referencing it worked. Now, I have to define it in every project that uses records/init – Josh Jul 22 '20 at 15:58
  • 3
    I had this error when TargetFramework netstandard2.0 with LangVersion 9.0. This class made it work. – Gábor Imre Sep 02 '20 at 07:38
  • 3
    I have 5.0 release version and it's still a problem wtf – Sinaesthetic Dec 01 '20 at 17:29
  • @Sinaesthetic I have the issue on only one out of 20 .NET 5 projects right now. Too busy to troubleshoot this. – Panagiotis Kanavos Dec 01 '20 at 17:58
  • Tested this fix with .NET Framework 4.5.2 and both `init` and records work. – Olivier Jacot-Descombes Dec 11 '20 at 13:32
  • @PanagiotisKanavos: Thanks! How in the blazes did you figure this out? – JohannSig Jan 10 '21 at 17:13
  • @JohannSig I don't remember. *After* I found out that LinqPad 6 worked, I tried to find what the extra type was. I think I found some GitHub issue that used this workaround, but I don't remember when that was. Or perhaps I searched GitHub for the type and found it was just empty? Having all source code in GitHub is a **huge** help – Panagiotis Kanavos Jan 10 '21 at 17:19
  • 2
    @PanagiotisKanavos: I experienced the same as you (having only one .NET 5.0 project out of a lot others that got this trouble on the records declaration). I found out that it happens on projects that have a reference to other projects where .NET Standard 2.0 is being used along with C#9.0 features like the "records" one. If I removed this project from the references, error goes away. As soon as I re-add the reference to the project, it reappears. It doesn't matter if you already added the class in the old project, you have to add it on current one as well. – CesarD Jan 13 '21 at 18:57
  • 1
    **Very important**: do *not* put `#if` around this declaration. See https://twitter.com/aarnott/status/1362786409954766858 for details. Also, make it *internal*. – Andrew Arnott Feb 19 '21 at 16:37
  • This will not allow using 'init only' properties in razor files. This is the error: Property, indexer, or event 'Model.MyProperty' is not supported by the language; try directly calling accessor methods – HamedH May 19 '21 at 15:51
  • 1
    There is a package that provides this and other classes that are required to make new C# features work in older target frameworks: [PolySharp](https://www.nuget.org/packages/PolySharp/) – cremor Jan 19 '23 at 06:45
20

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
Bouke
  • 11,768
  • 7
  • 68
  • 102
  • Nice answer but it needs to be explained better : 1 - In your root folder (solution folder). Create a file Directory.Build.props where *.pros is its extension. 2 - Edit this file and add : 3 - Add the IsExternalInit.cs file alongside – abdelgrib Feb 04 '22 at 09:48
  • This works for MSBuild, but VS even with a successful build inline-marks it as an error anyway? (targeting .NET FW, LangVersion 9.0) – Kissaki Apr 07 '22 at 15:31
  • @Kissaki I don't get IntelliSense issues on the `init` keyword in Visual Studio 2022. – Bouke Oct 11 '22 at 18:56
16

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;
}
Brandon Minnick
  • 13,342
  • 15
  • 65
  • 123
mycroes
  • 645
  • 8
  • 20
  • 1
    Can you explain why you would do this? – HackSlash Apr 04 '23 at 17:21
  • Because it still solves the problem and feels like the perfect balance between genius and stupid. – mycroes Apr 05 '23 at 05:26
  • Ok, I'm asking what benefit there is to `public record` over `internal class`. It seems to only reduce the type scope to record types while increasing the visibility scope to be external. Is that desirable? – HackSlash Apr 05 '23 at 15:30
  • The differences are negligible. As far as scope is concerned, choose what works best for you. I sometimes use this in a project that's referenced by almost any other project in the solution, in that case `public` avoids the need of copying. `record`s are basically `class`es, so choose whatever you prefer. – mycroes Apr 05 '23 at 20:15
4

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.

abdelgrib
  • 843
  • 7
  • 11
  • In case you're adding it to every project in the solution, it doesn't make sense to have it `public`. – mycroes Jul 31 '23 at 20:49