Code Monkey home page Code Monkey logo

isexternalinit's Introduction

IsExternalInit Nuget

Use C# 9's init and record features in older target frameworks.

๐Ÿƒ Quickstart ย  | ย  ๐Ÿ“ฆ NuGet


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 records 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.

Compatibility

IsExternalInit is currently compatible with the following target frameworks:

  • .NET Standard >= 1.0
  • .NET Framework >= 2.0

Quickstart

โš ๏ธ Important:
You must use a C# version >= 9.0 with the IsExternalInit 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.

  1. Reference the package
    Add the package to your project, for example via:

    Install-Package IsExternalInit
    
    --or--
    
    dotnet add package IsExternalInit
  2. 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.

  3. 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.

Compiler Constants

The included C# file makes use of some compiler constants that can be used to enable or disable certain features.

ISEXTERNALINIT_DISABLE

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.

ISEXTERNALINIT_INCLUDE_IN_CODE_COVERAGE

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.

Building

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 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.

Contributing

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!

License

See the LICENSE file for details.

isexternalinit's People

Contributors

arahaan avatar gtbuchanan avatar manuelroemer avatar skeller1 avatar sweini avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

isexternalinit's Issues

IsExternalInit must be declared for all TFMs

Unlike other polyfill packages where you can get away with declaring attributes only where the TFM does not declare them, IsExternalInit must be declared in the same assembly forever and for all TFMs or else it's a binary breaking change.

I see in your nuspec that you omit the declaration for net5 but you can't do that. You must declare it there as well.
The reason being the compiler generates metadata on the init property accessors that references this attribute using its assembly-qualified type name. When other assemblies reference this one and invoke an init accessor, the compiler embeds that exact same assembly-qualified reference into their invocation of that accessor. Now imagine this referencing library runs in a process where the referenced library is no longer the one that targeted net472 but targets net5. The CLR will throw a MissingMethodException when it encounters the init accessor because the attribute on the accessor does not match the IsExternalInit reference in the caller.

See proof of the breaking change

How about adding Range+Index to allow range syntax too?

Hey there,
I love your Nullable and IsExternalInit polyfills. I noticed there's another (slightly more involved) one that would be awesome to combine with those two: support for range syntax on NS2 (probably others too?).

The code needed to support that is basically https://github.com/devlooped/avatar/blob/main/src/Avatar.UnitTests/Range.cs (which I copy-pasted from corefx, since that was an earlier, simpler version), which you can see working on that net472 multi-targeting project. I use that same file in other NS2 projects too.

I'm just too lazy to clone the entire approach you already have, so I thought I might just suggest you do it instead, he :).

And FWIW, I think a meta-package that brought them all would be nice too :).

Predefined type 'System.Runtime.CompilerServices.IsExternalInit' is not defined or imported

When using the package I get this error with my records. (net461/netstandard2.0/netstandard2.1)

But additonally using

namespace System.Runtime.CompilerServices
{
    internal class IsExternalInit { }
}

will show that this is already included by the package.

When I build the solution by starting a project within the solution that references the project using IsExternalInit, it compiles. But when I compile the standalone project it shows this error for each record.

It used to work not too long ago, but now it doesn't, very strange. I'm using VS 17.2 and 17.3 Preview.

Only using my own IsExternalInit works every time, but I would like to use the package...

Use in .Net Framework 3.5

I'm trying to use this in a .Net Framework 3.5 project with the language version set to C#9, like so:

public record TestRecord
{
    public string Name { get; init; }
}

I am getting the following error:

[CS0656] Missing compiler required member 'System.Type.op_Equality'

Is it even possible to use C# 9 records in .Net Framework 3.5?

Rebuild of a solution targeting multiple frameworks fails on Visual Studio 17.2.0 & 17.2.1

It might be Microsoft's fault but on Visual Studio 17.2.0 & 17.2.1 a build of a solution targeting multiple frameworks and containing your package works fine, whereas a rebuild always fails with "Predefined type 'System.Runtime.CompilerServices.IsExternalInit' is not defined or imported".

It can easily be reproduced with your test project PackageReference.MultiTarget.
Clean & Build works fine.
Rebuild fails.

Duplicate definition error in VS 2022

I'm trying to upgrade my solution to VS 2022, but if I build it I'm getting duplicate definition errors that weren't there before with VS 2019. I guess this is caused by the new .net 6 sdk?

I think a simple future proof fix would be to wrap the source code in another #if block

#if !NET
...
#endif

since NET is defined for .net 5 and later.

Why is this needed?

I don't understand.
I have a netstandard2.0 assembly using LangVersion 9.0 and with this added:
namespace System.Runtime.CompilerServices
{
internal class IsExternalInit { }
}
Then init works.

I can reference this assembly from net5.0 assemblies (using default LangVersion 9.0) and from .NET framework 4.7.2 using default LangVersion (assume 7.3). So why would I need this?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.