ltrzesniewski / Polyfill

Source only package that exposes newer .net and C# features to older runtimes.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Polyfill

Build status Polyfill NuGet Status

Source only package that exposes newer .net and C# features to older runtimes.

The package targets netstandard2.0 and is designed to support the following runtimes.

  • net461, net462, net47, net471, net472, net48, net481
  • netcoreapp2.0, netcoreapp2.1, netcoreapp3.0, netcoreapp3.1
  • net5.0, net6.0, net7.0, net8.0

Nuget

https://nuget.org/packages/Polyfill/

SDK / LangVersion

This project leverages features the current stable SDK and c# language. As such consuming projects should target those:

LangVersion

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <LangVersion>latest</LangVersion>

global.json

{
  "sdk": {
    "version": "7.0.202",
    "rollForward": "latestFeature"
  }
}

Included polyfills

ModuleInitializerAttribute

Reference: Module Initializers

static bool InitCalled;

[Test]
public void ModuleInitTest() =>
    Assert.True(InitCalled);

[ModuleInitializer]
public static void ModuleInit() =>
    InitCalled = true;

snippet source | anchor

IsExternalInit

Reference: init (C# Reference)

class InitExample
{
    private int member;

    public int Member
    {
        get => member;
        init => member = value;
    }
}

snippet source | anchor

Nullable attributes

Reference: Nullable reference types

  • [AllowNull]
  • [DisallowNull]
  • [DoesNotReturn]
  • [DoesNotReturnIf]
  • [MaybeNull]
  • [MaybeNullWhen]
  • [MemberNotNull]
  • [MemberNotNullWhen]
  • [NotNull]
  • [NotNullIfNotNull]
  • [NotNullWhen]

Required attributes

Reference: C# required modifier

public class Person
{
    public Person() { }

    [SetsRequiredMembers]
    public Person(string name) =>
        Name = name;

    public required string Name { get; init; }
}

snippet source | anchor

CompilerFeatureRequiredAttribute

Reference: CompilerFeatureRequiredAttribute

Indicates that compiler support for a particular feature is required for the location where this attribute is applied.

SkipLocalsInit attribute

Reference: (SkipLocalsInit attribute)(https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/attributes/general#skiplocalsinit-attribute)

the SkipLocalsInit attribute prevents the compiler from setting the .locals init flag when emitting to metadata. The SkipLocalsInit attribute is a single-use attribute and can be applied to a method, a property, a class, a struct, an interface, or a module, but not to an assembly. SkipLocalsInit is an alias for SkipLocalsInitAttribute.

class SkipLocalsInitSample
{
    [SkipLocalsInit]
    static void ReadUninitializedMemory()
    {
        Span<int> numbers = stackalloc int[120];
        for (int i = 0; i < 120; i++)
        {
            Console.WriteLine(numbers[i]);
        }
    }
}

snippet source | anchor

Index and Range

Reference: Indices and ranges

[TestFixture]
class IndexRangeSample
{
    [Test]
    public void Range()
    {
        var substring = "value"[2..];
        Assert.AreEqual("lue", substring);
    }

    [Test]
    public void Index()
    {
        var ch = "value"[^2];
        Assert.AreEqual('u', ch);
    }
}

snippet source | anchor

CallerArgumentExpressionAttribute

Reference: CallerArgumentExpression

using System.IO;

static class Guard
{
    public static void FileExists(string path, [CallerArgumentExpression("path")] string argumentName = "")
    {
        if (!File.Exists(path))
        {
            throw new ArgumentException($"File not found. Path: {path}", argumentName);
        }
    }
}

static class GuardUsage
{
    public static string[] Method(string path)
    {
        Guard.FileExists(path);
        return File.ReadAllLines(path);
    }
}

snippet source | anchor

Alternatives

PolySharp

https://github.com/Sergio0694/PolySharp

Combination of

Reason this project was created instead of using the above

PolySharp uses c# source generators. In my opinion a "source-only package" implementation is better because:

  • Simpler implementation
  • Easier to debug if something goes wrong.
  • Uses less memory at compile time. Since there is no source generator assembly to load.
  • Faster at compile time. Since no source generator is required to execute.

The combination of the other 3 packages is not ideal because:

  • Required multiple packages to be referenced.
  • Does not cover all the scenarios included in this package.

Extensions

The class Polyfill.PolyExtensions includes the following extension methods:

  • bool StartsWith(this string value, char ch)
  • bool EndsWith(this string value, char ch)
  • bool Contains(this ReadOnlySpan<char> span, char value)
  • void Append(this StringBuilder builder, ReadOnlySpan<char> value)
  • bool SequenceEqual(this ReadOnlySpan<char> span, string other)
  • bool Equals(this StringBuilder builder, ReadOnlySpan<char> span)
  • bool SequenceEqual(this Span<char> span, string other)

References

System.ValueTuple

If consuming in a project that targets net461 or net462, a reference to System.ValueTuple nuget is required.

<PackageReference Include="System.ValueTuple"
                  Version="4.5.0"
                  Condition="'$(TargetFramework)' == 'net461' OR '$(TargetFramework)' == 'net462'" />

System.Memory

If consuming in a project that targets netstandard, netframework, or netcoreapp, a reference to System.Memory nuget is required.

<PackageReference Include="System.Memory"
                  Version="4.5.5"
                  Condition="'$(TargetFrameworkIdentifier)' == '.NETStandard' OR '$(TargetFrameworkIdentifier)' == '.NETFramework' OR '$(TargetFrameworkIdentifier)' == '.NETCOREAPP'" />

Icon

Crack designed by Adrien Coquet from The Noun Project.

About

Source only package that exposes newer .net and C# features to older runtimes.

License:MIT License


Languages

Language:C# 100.0%