Polyfill

repository·main·Indexed 19 days ago

https://github.com/simoncropp/polyfill

A source-only package that provides newer .NET and C# features to older runtimes, targeting netstandard2.0. It enables modern syntax and APIs—including Index and Range syntax, required members, and various attributes (ModuleInitializer, IsExternalInit, CallerArgumentExpression)—across .NET Framework, .NET Core, .NET 5+, and UWP. It also provides extension methods for ArgumentException, ArraySegment<T>, Base64, and BinaryPrimitives, as well as missing members for CancellationToken and concurrent collections.

Tokens
34.5K
Snippets
74
Records
171
Agent score
65%

What's inside Polyfill

  1. Overview of Polyfill

    main
    Polyfill is a source-only package designed to expose newer .NET and C# features to older runtimes. It targets netstandard2.0 and provides a wide range of APIs across various frameworks, including .NET Framework, .NET Core, .NET 5+, and UWP.
  2. Key Features of Polyfill

    main

    Polyfill provides several mechanisms to bridge the gap between runtime versions:

    • Source-only integration: Compiles directly into your project; no runtime dependency is required.
    • Extensive API coverage: Includes 881 polyfilled APIs across attributes, extension methods, and static helpers.
    • Conditional compilation: Only includes APIs that are missing from your specific target framework.
    • Optional feature groups: Provides specialized functionality for Ensure, Guard, Nullability, ArgumentExceptions, and StringInterpolation.
  3. Understand the Enumerable.Reverse array polyfill

    main

    Due to breaking changes in C# 14/dotnet 10 regarding how array.Reverse() is resolved (shifting from Enumerable.Reverse to an in-place Span reversal), this package includes a polyfill for the Enumerable.Reverse<T>(this T[]) overload on older target frameworks.

    This polyfill is located in the System.Linq namespace to ensure it takes precedence over Polyfills (which is available via global using) when resolving extension methods from using System;.

  4. Understand the performance and size impacts of missing multi-targeting

    main

    Failure to multi-target when using Polyfill results in three main negative impacts:

    1. Performance: Polyfills may use less efficient implementations than the native runtime. For example, the polyfill for StringBuilder.Append(ReadOnlySpan<char>) on netcore2 performs a ToString() call, which causes an unnecessary string allocation.
    2. Assembly Size: Without multi-targeting, redundant polyfill IL is bundled into higher TFMs. An assembly using Polyfill features might grow from ~50 KB on net10 to ~230 KB on net461 if it is not properly multi-targeted.
    3. Load Time and Memory: Larger assemblies with more IL take longer to load and require more memory for JIT compilation.
  5. How to consume Polyfill to avoid type conflicts

    main

    By default, all Polyfill types are marked as internal. This allows the package to be used across multiple projects without causing type name conflicts.

    If your projects use InternalsVisibleTo (e.g., for unit testing), you may encounter type visibility conflicts. To resolve this, use the Embedded pattern: use the Polyfill source package and set <PolyUseEmbeddedAttribute>true</PolyUseEmbeddedAttribute> in a <PropertyGroup>. This ensures that the types are treated as truly embedded and remain invisible to other assemblies even when InternalsVisibleTo is present.

    <PropertyGroup>
      <PolyUseEmbeddedAttribute>true</PolyUseEmbeddedAttribute>
    </PropertyGroup>
  6. Configure global.json for Polyfill consumption

    main

    To ensure compatibility with the features used by Polyfill, it is recommended to use a recent SDK via a global.json file.

    {
      "sdk": {
        "version": "10.0.300",
        "rollForward": "latestFeature"
      }
    }
  7. Consume Polyfill in an application project

    main

    When working in a solution that produces an application, the recommended approach is to add the Polyfill NuGet package only to the root app project and enable the PolyPublic property.

    By setting <PolyPublic>true</PolyPublic>, the polyfills become available to all downstream projects in the solution (such as test projects) without requiring them to reference the Polyfill NuGet package directly.

    <Project Sdk="Microsoft.NET.Sdk">
      <PropertyGroup>
        <PolyPublic>true</PolyPublic>
      </PropertyGroup>
    </Project>
  8. Install Polyfill via NuGet

    main

    Polyfill is a source-only package that provides newer .NET and C# features to older runtimes. Because it is source-only, it compiles directly into your consuming project and does not create a runtime dependency.

    To use it, add the Polyfill package to your project. It is recommended to set your <LangVersion> to latest to take advantage of the newer C# features being polyfilled.

    <Project Sdk="Microsoft.NET.Sdk">
      <PropertyGroup>
        <LangVersion>latest</LangVersion>
      </PropertyGroup>
      <ItemGroup>
        <PackageReference Include="Polyfill" Version="*" PrivateAssets="all" />
      </ItemGroup>
    </Project>
  9. Enable InterpolatedStringHandler via MSBuild

    main

    To enable support for custom interpolated string handlers (like AppendInterpolatedStringHandler and DefaultInterpolatedStringHandler), add the PolyStringInterpolation property to your MSBuild project file:

    <PropertyGroup>
      <PolyStringInterpolation>true</PolyStringInterpolation>
    </PropertyGroup>
  10. Consume Polyfill in a library project

    main

    There are two ways to consume Polyfill when building a library:

    1. Standard Library: If you are not using InternalsVisibleTo to expose APIs, you can add the Polyfill NuGet package to all individual projects in your solution.
    2. Library with InternalsVisibleTo: If you use InternalsVisibleTo (for example, to expose internal APIs to a test project), you should add the Polyfill NuGet package only to the root library project to avoid visibility issues.
  11. Configure required NuGet references for polyfills

    main

    Certain polyfills require specific NuGet packages to be present in your project. If these references are missing, the related polyfills will be disabled.

    System.ValueTuple

    Required if targeting net461 or net462.

    System.Memory

    Required if using Span APIs and targeting netstandard, netframework, or netcoreapp2*. Warning: If System.Memory is installed at a version older than 4.5.5, a build warning PolyfillMemoryVersion will be raised. Older versions lack the types required for FeatureMemory to be enabled, which can cause polyfills to fail silently.

    System.Threading.Tasks.Extensions

    Required if using ValueTask APIs and targeting netframework, netstandard2, or netcoreapp2.

    System.Runtime.InteropServices

    Required if using RuntimeInformation or OSPlatform and targeting netframework.

    <!-- System.ValueTuple -->
    <PackageReference Include="System.ValueTuple" Version="4.5.0" Condition="$(TargetFramework.StartsWith('net46'))" />
    
    <!-- System.Memory -->
    <PackageReference Include="System.Memory" Version="4.5.5" Condition="$(TargetFrameworkIdentifier) == '.NETStandard' or $(TargetFrameworkIdentifier) == '.NETFramework' or $(TargetFramework.StartsWith('netcoreapp2'))" />
    
    <!-- System.Threading.Tasks.Extensions -->
    <PackageReference Include="System.Threading.Tasks.Extensions" Version="4.5.4" Condition="$(TargetFramework) == 'netstandard2.0' or $(TargetFramework) == 'netcoreapp2.0' or $(TargetFrameworkIdentifier) == '.NETFramework'" />
    
    <!-- System.Runtime.InteropServices.RuntimeInformation -->
    <PackageReference Include="System.Runtime.InteropServices.RuntimeInformation" Version="4.3.0" Condition="$(TargetFrameworkIdentifier) == '.NETFramework'" />