Library / SDK
Sergio0694/PolySharp avatar
Sergio0694/PolySharp

PolySharp: source-only polyfills that let old .NET runtimes compile modern C#

PolySharp provides generated, source-only polyfills for C# language features, to easily use all runtime-agnostic features downlevel. Add a reference, set your C# version to latest and have fun! 🚀

2,236 stars72 forksC#MIT

At a glance

What is it?
A Roslyn source generator that emits the attribute and marker types the C# compiler insists on seeing, so language features from C# 14 work on frameworks that predate them by a decade.
Who is it for?
PolySharp exists because of one specific and quite sharp constraint: the C# compiler will not let you use a language feature unless the types it depends on are visible, and on an older target framework those types simply are not there. Emitting them as source in your own assembly sidesteps that entirely, with no runtime dependency and nothing to deploy.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 139 days ago.
What is it written in?
Mainly C#, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 11, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The compiler wants to see types, so generate them

The problem PolySharp solves is stated more precisely than most libraries state their own. Modern C# features are gated on types that ship in newer base class libraries, not on the language version alone. Write `[NotNullWhen(true)]` on an older target framework and the compiler reports a missing type, regardless of how new your language version is set. The fix is not a runtime library but generated source: PolySharp emits those types into your own assembly as internal declarations, and the compiler finds them where it expects to.

That is why the project describes itself as generated, source-only polyfills rather than a NuGet package with a DLL. The README is direct about the deal: add a reference to PolySharp, set your C# language version to latest, and the generator detects which polyfills are needed for the target framework and project in use. The topics on the repository make the mechanism explicit, listing roslyn, roslyn-generator and roslyn-analyzer alongside the broader dotnet tags. Nothing extra runs at application start because there is nothing runtime to initialise; the generated types exist to satisfy the compiler, and on frameworks that already have the real types the generator simply does not emit them.

Turning it on is one MSBuild property

Configuration is minimal by design. The README states that you bump the C# language version by setting the `<LangVersion>` MSBuild property in your project, giving `<LangVersion>14.0</LangVersion>` as the example, added to the first `<PropertyGroup>` of your `.csproj` file. The point it emphasises is what you no longer need to do: there is no manual copying of polyfill sources any more, because referencing the package handles it.

The project files themselves show a modern .NET setup. The tree is small and tidy, with `src/` and `tests/`, a `global.json` pinning the SDK, shared `Directory.Build.props` and `Directory.Build.targets`, an `.editorconfig`, and a solution named `PolySharp.slnx`. That `.slnx` extension is the newer XML solution format, and the 1.16.0 release notes record migrating the solution to it, which tells you the build tooling has been kept current even though the package targets older runtimes. The MIT licence file is present at the root and GitHub reports MIT, so there is no licensing ambiguity here.

What the generator covers

The bulk of the README is a catalogue, and the catalogue is the documentation. The nullability attributes come first and are the largest group: `[AllowNull]`, `[DisallowNull]`, `[DoesNotReturn]`, `[DoesNotReturnIf]`, `[MaybeNull]`, `[MaybeNullWhen]`, `[MemberNotNull]`, `[MemberNotNullWhen]`, `[NotNull]`, `[NotNullIfNotNull]` and `[NotNullWhen]`. These are what make nullable reference type annotations work on frameworks that predate them.

After that come structural types and markers. `Index` and `Range` back the indices and ranges feature. `[IsExternalInit]` is required for init-only properties, `[RequiredMember]` and `[SetsRequiredMembers]` for the required modifier, and `[CompilerFeatureRequired]` supports several features at once. Newer entries track recent language versions: `[CollectionBuilder]` and `[CompilerLoweringPreserve]` for collection expressions, `[OverloadResolutionPriority]` and `[ParamsCollection]` and `[ConstantExpected]` for C# 13, and `[ExtensionMarker]` for the C# 14 extension members feature.

A second group is described as optional runtime-supported polyfills, and it is worth noting that these are opt in rather than automatic: reflection annotation attributes such as `[DynamicallyAccessedMembers]`, `[DynamicDependency]`, `[RequiresUnreferencedCode]` and `[RequiresDynamicCode]`, plus `[StackTraceHidden]` and `[UnmanagedCallersOnly]`. That distinction matters for trimming work, since these attributes only matter if you are preparing a library for trimming or AOT.

The limits are documented, which is unusual and useful

Two notes in the README draw boundaries, and being explicit about them is probably the strongest signal of how the project is maintained. The first says not all new C# features can be worked around this way: features requiring genuine runtime support, with static abstract interface members given as the example, still will not work. That is not really a limitation of PolySharp, since no amount of generated source can conjure a runtime dispatch mechanism, but the README declines to bury the point.

The second note is about .NET Framework 3.5, called out as particularly limited because of shortcomings in the base class library there. The specifics are given: `System.Range` will not be generated unless `System.ValueTuple` is referenced, which means list patterns do not work out of the box, and some features such as records are entirely unusable because the compiler is missing APIs that cannot be polyfilled. Everything else is said to work as expected. That is the kind of version-by-version guidance that saves hours when you are the person responsible for a legacy target.

The release history shows the project tracking language versions rather than adding features of its own. Version 1.16.0 in May 2026 adds C# 14 support with .NET 10 attributes, and its fix list is instructive about how the generator interacts with other tooling: emitting an `[Embedded]` attribute for all polyfills, and fixing a case where polyfills were not generated when `InternalsVisibleTo` was combined with `EmbeddedAttribute`. Version 1.15.0 added C# 13 support, and 1.14.1 fixed `InlineArrayAttribute` visibility. Contributors beyond the maintainer appear in these entries too, including a fix to `PolyfillsGenerator.cs` and the first contributions behind the .NET 9 attributes.

Editorial conclusion

PolySharp exists because of one specific and quite sharp constraint: the C# compiler will not let you use a language feature unless the types it depends on are visible, and on an older target framework those types simply are not there. Emitting them as source in your own assembly sidesteps that entirely, with no runtime dependency and nothing to deploy. The list covers the nullability attributes, Index and Range, init-only property support, required members, collection expression support and a long tail of annotation attributes, and the generator only emits what your project actually needs. The limits are stated plainly rather than hidden: features requiring runtime support such as static abstract interface members will not work, and .NET Framework 3.5 is called out as particularly constrained, where records are unusable and list patterns need ValueTuple referenced first. For anyone maintaining mixed-framework code, this turns a long tail of conditional compilation into one package reference.

Frequently asked questions

What problem does PolySharp solve?

The C# compiler refuses to let you use a language feature unless the types it depends on are visible. PolySharp generates those missing types as source in your project so features like nullability annotations work on older frameworks.

How do I enable PolySharp in a project?

Reference the package and set the LangVersion MSBuild property, for example LangVersion 14.0, in the first PropertyGroup of your csproj. No polyfill sources need to be copied manually.

Which C# features can PolySharp not enable?

Features that need real runtime support cannot be polyfilled, with static abstract interface members named as the example. On .NET Framework 3.5, records are unusable and list patterns require ValueTuple to be referenced first.

Does PolySharp add anything at runtime?

No. It is a source generator that emits types into your own assembly so the compiler can see them. On frameworks that already provide the real types, nothing is emitted for those.

Official sources

  1. Issues
  2. License: MIT
  3. README
  4. Releases
  5. Sergio0694/PolySharp on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/sergio0694-polysharp.svg)](https://hysenlabs.com/projects/sergio0694-polysharp)