Use C# 9's init and record features in in older target frameworks.
You may also want to check out my Nullable project which provides support for .NET's nullable reference type attributes for older target frameworks.
C# 9 added support for the init
and record
keywords. When using C# 9 with target frameworks
<= .NET 5.0, using these new features is not possible because the compiler is missing the
IsExternalInit
class, hence making the features unavailable for any target framework prior to
.NET 5.
Luckily, this problem can be solved by re-declaring the IsExternalInit
class as an
internal class
in your own project. The compiler will use this custom class definition and thus
allow you to use both the init
keywords and record
s in any project.
This repository hosts the code for the "IsExternalInit" NuGet Package
which, when referenced, automatically adds the IsExternalInit
class to the referenced project(s).
The code for the IsExternalInit
class is added at compile time and gets built into the referencing project.
This means that the resulting project does not have an explicit dependency on the IsExternalInit
package, because the code is not distributed as a standard library.
IsExternalInit is currently compatible with the following target frameworks:
- .NET Standard >= 1.0
- .NET Framework >= 2.0
⚠️ Important:
You must use a C# version >= 9.0 with theIsExternalInit
package - otherwise, your project won't compile.
The steps below assume that you are using the new SDK .csproj
style.
Please find installation guides and notes for other project types (for example packages.config
)
here.
-
Reference the package
Add the package to your project, for example via:Install-Package IsExternalInit --or-- dotnet add package IsExternalInit
-
Ensure that the package has been added as a development dependency
Open your.csproj
file and ensure that the new package reference looks similar to this:<PackageReference Include="IsExternalInit" Version="<YOUR_VERSION>" PrivateAssets="all" /> <!-- NuGet, by default, uses this style. This is also acceptable. --> <PackageReference Include="IsExternalInit" Version="<YOUR_VERSION>"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> </PackageReference>
This is especially important for libraries that are published to NuGet, because without this, the library will have an explicit dependency on the
IsExternalInit
package. -
Build the project
Ensure that the project compiles. If a build error occurs, you will most likely have to update the C# language version.
Afterwards, you can immediately start using the attributes.
The included C# file makes use of some compiler constants that can be used to enable or disable certain features.
If the ISEXTERNALINIT_DISABLE
constant is defined, the IsExternalInit
class is excluded from the build.
This can be used to conditionally exclude code of this package from the build if it is not required.
In most cases, this should not be required, because the package automatically excludes the code
from target frameworks that already support the IsExternalInit
class.
Because the IsExternalInit
class is added as source code, it could appear in code coverage reports.
By default, this is disabled via the ExcludeFromCodeCoverage
and DebuggerNonUserCode
attributes.
By defining the ISEXTERNALINIT_INCLUDE_IN_CODE_COVERAGE
constant, the ExcludeFromCodeCoverage
and DebuggerNonUserCode
attributes are not applied and the IsExternalInit
class may therefore appear in code coverage reports.
Because the package consists of source files, building works differently than a normal .NET project.
In essence, no build has to be made at all. Instead, the *.cs
files are renamed to *.cs.pp
(because otherwise, Visual Studio's solution explorer would display the files in a project which
references the package) and then packaged into a NuGet package via a .nuspec
file.
The solution contains a _build
project which automatically performs these tasks though. You can then
find the resulting NuGet package file in the artifacts
folder.
I don't expect this package to require many changes, but if something is not working for you or if you think that the source file should change, feel free to create an issue or Pull Request. I will be happy to discuss and potentially integrate your ideas!
See the LICENSE file for details.