Skip to content

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

License

Notifications You must be signed in to change notification settings

SimonCropp/Polyfill

Repository files navigation

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, net9.0

API count: 410

See Milestones for release notes.

TargetFrameworks

Some polyfills are implemented in a way that will not have the equivalent performance to the actual implementations.

For example the polyfill for StringBuilder.Append(ReadOnlySpan<char>) on netcore2 is:

public StringBuilder Append(ReadOnlySpan<char> value)
    => target.Append(value.ToString());

Which will result in a string allocation.

As Polyfill is implemented as a source only nuget, the implementation for each polyfill is compiled into the IL of the resulting assembly. As a side-effect that implementation will continue to be used even if that assembly is executed in a runtime that has a more efficient implementation available.

As a result, in the context of a project producing nuget package, that project should target all frameworks from the lowest TargetFramework up to and including the current framework. This way the most performant implementation will be used for each runtime. Take the following examples:

  • If a nuget's minimum target is net6, then the resulting TargetFrameworks should also include net7.0 and net8.0
  • If a nuget's minimum target is net471, then the resulting TargetFrameworks should also include net472 and net48"

Nuget

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

SDK / LangVersion

This project uses features from 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": "8.0.301",
    "rollForward": "latestFeature"
  }
}

Consuming and type visibility

The default type visibility for all polyfills is internal. This means it can be consumed in multiple projects and types will not conflict.

Consuming in an app

If Polyfill is being consumed in a solution that produce an app, then it is recommended to use the Polyfill nuget only in the root "app project" and enable PolyPublic.

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <PolyPublic>true</PolyPublic>

Then all consuming projects, like tests, will not need to use the Polyfill nuget.

Consuming in a library

If Polyfill is being consumed in a solution that produce a library (and usually a nuget), then the Polyfill nuget can be added to all projects.

If, however, InternalsVisibleTo is being used to expose APIs (for example to test projects), then the Polyfill nuget should be added only to the root library project.

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 InitSample
{
    public int Member { get; init; }
}

snippet source | anchor

Nullable attributes

Reference: Nullable reference types

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

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

CollectionBuilderAttribute

Can be used to make types compatible with collection expressions

ConstantExpectedAttribute

Indicates that the specified method parameter expects a constant.

SkipLocalsInit

Reference: SkipLocalsInitAttribute

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 (var i = 0; i < 120; i++)
        {
            Console.WriteLine(numbers[i]);
        }
    }
}

snippet source | anchor

Index and Range

Reference: Indices and ranges

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

[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);
    }

    [Test]
    public void ArrayIndex()
    {
        var array = new[]
        {
            "value1",
            "value2"
        };

        var value = array[^2];

        Assert.AreEqual("value1", value);
    }
}

snippet source | anchor

OverloadResolutionPriority

C# introduces a new attribute, System.Runtime.CompilerServices.OverloadResolutionPriority, that can be used by API authors to adjust the relative priority of overloads within a single type as a means of steering API consumers to use specific APIs, even if those APIs would normally be considered ambiguous or otherwise not be chosen by C#'s overload resolution rules. This helps framework and library authors guide API usage as they APIs as they develop new and better patterns.

The OverloadResolutionPriorityAttribute can be used in conjunction with the ObsoleteAttribute. A library author may mark properties, methods, types and other programming elements as obsolete, while leaving them in place for backwards compatibility. Using programming elements marked with the ObsoleteAttribute will result in compiler warnings or errors. However, the type or member is still visible to overload resolution and may be selected over a better overload or cause an ambiguity failure. The OverloadResolutionPriorityAttribute lets library authors fix these problems by lowering the priority of obsolete members when there are better alternatives.

Usage

[TestFixture]
public class OverloadResolutionPriorityAttributeTests
{
    [Test]
    public void Run()
    {
        int[] arr = [1, 2, 3];
        //Prints "Span" because resolution priority is higher
        Method(arr);
    }

    [OverloadResolutionPriority(2)]
    static void Method(ReadOnlySpan<int> list) =>
        Console.WriteLine("Span");

    [OverloadResolutionPriority(1)]
    static void Method(int[] list) =>
        Console.WriteLine("Array");
}

snippet source | anchor

UnscopedRefAttribute

Reference: Low Level Struct Improvements

using System.Diagnostics.CodeAnalysis;
#pragma warning disable CS0169 // Field is never used

struct UnscopedRefUsage
{
    int field1;

    [UnscopedRef] ref int Prop1 => ref field1;
}

snippet source | anchor

RequiresPreviewFeaturesAttribute

CallerArgumentExpressionAttribute

Reference: CallerArgumentExpression

static class FileUtil
{
    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 FileUtilUsage
{
    public static string[] Method(string path)
    {
        FileUtil.FileExists(path);
        return File.ReadAllLines(path);
    }
}

snippet source | anchor

InterpolatedStringHandler

References: String Interpolation in C# 10 and .NET 6, Write a custom string interpolation handler

StringSyntaxAttribute

Reference: .NET 7 - The StringSyntaxAttribute

Trimming annotation attributes

Reference: Prepare .NET libraries for trimming

Platform compatibility

Reference: Platform compatibility analyzer

StackTraceHiddenAttribute

Reference: C# – Hide a method from the stack trace

UnmanagedCallersOnly

Reference: Improvements in native code interop in .NET 5.0

SuppressGCTransition

DisableRuntimeMarshalling

Extensions

The class Polyfill includes the following extension methods:

Important

The methods using AppendInterpolatedStringHandler parameter are not extensions because the compiler prefers to use the overload with string parameter instead.

Extension methods

bool

byte

  • bool TryFormat(Span<char>, int, ReadOnlySpan<char>, IFormatProvider?) reference

CancellationToken

  • CancellationTokenRegistration Register(Action<object?, CancellationToken>, object?) reference
  • CancellationTokenRegistration UnsafeRegister(Action<object?>, object?) reference
  • CancellationTokenRegistration UnsafeRegister(Action<object?, CancellationToken>, object?) reference

CancellationTokenSource

ConcurrentBag

ConcurrentDictionary<TKey, TValue>

  • TValue GetOrAdd<TKey, TValue, TArg>(TKey, Func<TKey, TArg, TValue>, TArg) where TKey : notnull reference

ConcurrentQueue

DateOnly

  • bool TryFormat(Span<char>, int, ReadOnlySpan<char>, IFormatProvider?) reference

DateTime

DateTimeOffset

  • DateTimeOffset AddMicroseconds(double) reference
  • int Microsecond() reference
  • int Nanosecond() reference
  • bool TryFormat(Span<char>, int, ReadOnlySpan<char>, IFormatProvider?) reference

decimal

  • bool TryFormat(Span<char>, int, ReadOnlySpan<char>, IFormatProvider?) reference

Dictionary<TKey, TValue>

  • bool Remove<TKey, TValue>(TKey, TValue) where TKey : notnull reference
  • bool TryAdd<TKey, TValue>(TKey, TValue) where TKey : notnull reference

double

  • bool TryFormat(Span<char>, int, ReadOnlySpan<char>, IFormatProvider?) reference

EventInfo

  • NullabilityState GetNullability()
  • NullabilityInfo GetNullabilityInfo()
  • bool IsNullable()

FieldInfo

  • NullabilityState GetNullability()
  • NullabilityInfo GetNullabilityInfo()
  • bool IsNullable()

float

  • bool TryFormat(Span<char>, int, ReadOnlySpan<char>, IFormatProvider?) reference

Guid

  • bool TryFormat(Span<char>, int, ReadOnlySpan<char>) reference

HashSet

HttpClient

  • Task<byte[]> GetByteArrayAsync(string, CancellationToken) reference
  • Task<byte[]> GetByteArrayAsync(Uri, CancellationToken) reference
  • Task<Stream> GetStreamAsync(string, CancellationToken) reference
  • Task<Stream> GetStreamAsync(Uri, CancellationToken) reference
  • Task<string> GetStringAsync(string, CancellationToken) reference
  • Task<string> GetStringAsync(Uri, CancellationToken) reference

HttpContent

  • Task<byte[]> ReadAsByteArrayAsync(CancellationToken) reference
  • Task<Stream> ReadAsStreamAsync(CancellationToken) reference
  • Task<string> ReadAsStringAsync(CancellationToken) reference

IDictionary<TKey, TValue>

  • ReadOnlyDictionary<TKey, TValue> AsReadOnly<TKey, TValue>() where TKey : notnull reference

IEnumerable

  • IEnumerable<(TFirst First, TSecond Second, TThird Third)> Zip<TFirst, TSecond, TThird>(IEnumerable<TSecond>, IEnumerable<TThird>) reference
  • IEnumerable<(TFirst First, TSecond Second)> Zip<TFirst, TSecond>(IEnumerable<TSecond>) reference

IEnumerable

  • IEnumerable<KeyValuePair<TKey, TAccumulate>> AggregateBy<TSource, TKey, TAccumulate>(Func<TSource, TKey>, TAccumulate, Func<TAccumulate, TSource, TAccumulate>, IEqualityComparer<TKey>?) where TKey : notnull reference
  • IEnumerable<KeyValuePair<TKey, TAccumulate>> AggregateBy<TSource, TKey, TAccumulate>(Func<TSource, TKey>, Func<TKey, TAccumulate>, Func<TAccumulate, TSource, TAccumulate>, IEqualityComparer<TKey>?) where TKey : notnull reference
  • IEnumerable<TSource> Append<TSource>(TSource) reference
  • IEnumerable<TSource[]> Chunk<TSource>(int) reference
  • IEnumerable<KeyValuePair<TKey, int>> CountBy<TSource, TKey>(Func<TSource, TKey>, IEqualityComparer<TKey>?) where TKey : notnull reference
  • IEnumerable<TSource> DistinctBy<TSource, TKey>(Func<TSource, TKey>) reference
  • IEnumerable<TSource> DistinctBy<TSource, TKey>(Func<TSource, TKey>, IEqualityComparer<TKey>) reference
  • TSource ElementAt<TSource>(Index) reference
  • TSource? ElementAtOrDefault<TSource>(Index) reference
  • IEnumerable<TSource> Except<TSource>(TSource) reference
  • IEnumerable<TSource> Except<TSource>(TSource, IEqualityComparer<TSource>?) reference
  • IEnumerable<TSource> Except<TSource>(IEqualityComparer<TSource>, TSource[]) reference
  • IEnumerable<TSource> ExceptBy<TSource, TKey>(IEnumerable<TKey>, Func<TSource, TKey>) reference
  • IEnumerable<TSource> ExceptBy<TSource, TKey>(IEnumerable<TKey>, Func<TSource, TKey>, IEqualityComparer<TKey>?) reference
  • TSource FirstOrDefault<TSource>(Func<TSource, bool>, TSource) reference
  • TSource FirstOrDefault<TSource>(TSource) reference
  • IEnumerable<(int Index, TSource Item)> Index<TSource>() reference
  • TSource LastOrDefault<TSource>(TSource) reference
  • TSource LastOrDefault<TSource>(Func<TSource, bool>, TSource) reference
  • TSource? Max<TSource>(IComparer<TSource>?) reference
  • TSource? MaxBy<TSource, TKey>(Func<TSource, TKey>) reference
  • TSource? MaxBy<TSource, TKey>(Func<TSource, TKey>, IComparer<TKey>?) reference
  • TSource? Min<TSource>(IComparer<TSource>?) reference
  • TSource? MinBy<TSource, TKey>(Func<TSource, TKey>) reference
  • TSource? MinBy<TSource, TKey>(Func<TSource, TKey>, IComparer<TKey>?) reference
  • TSource SingleOrDefault<TSource>(Func<TSource, bool>, TSource) reference
  • TSource SingleOrDefault<TSource>(TSource) reference
  • IEnumerable<TSource> SkipLast<TSource>(int) reference
  • IEnumerable<TSource> Take<TSource>(Range) reference
  • IEnumerable<TSource> TakeLast<TSource>(int) reference
  • HashSet<TSource> ToHashSet<TSource>(IEqualityComparer<TSource>?) reference
  • bool TryGetNonEnumeratedCount<TSource>(int) reference

IList

  • ReadOnlyCollection<T> AsReadOnly<T>() reference

int

  • bool TryFormat(Span<char>, int, ReadOnlySpan<char>, IFormatProvider?) reference

IReadOnlyDictionary<TKey, TValue>

  • TValue? GetValueOrDefault<TKey, TValue>(TKey) where TKey : notnull reference
  • TValue GetValueOrDefault<TKey, TValue>(TKey, TValue) where TKey : notnull reference

KeyValuePair<TKey, TValue>

  • void Deconstruct<TKey, TValue>(TKey, TValue) reference

List

long

  • bool TryFormat(Span<char>, int, ReadOnlySpan<char>, IFormatProvider?) reference

MemberInfo

  • NullabilityState GetNullability()
  • NullabilityInfo GetNullabilityInfo()
  • bool HasSameMetadataDefinitionAs(MemberInfo) reference
  • bool IsNullable()

ParameterInfo

  • NullabilityState GetNullability()
  • NullabilityInfo GetNullabilityInfo()
  • bool IsNullable()

Process

  • Task WaitForExitAsync(CancellationToken) reference

PropertyInfo

  • NullabilityState GetNullability()
  • NullabilityInfo GetNullabilityInfo()
  • bool IsNullable()

Random

ReadOnlySpan

  • bool EndsWith(string, StringComparison) reference
  • SpanLineEnumerator EnumerateLines() reference
  • bool SequenceEqual(string) reference
  • bool StartsWith(string, StringComparison) reference

ReadOnlySpan

  • bool Contains<T>(T) where T : IEquatable<T> reference
  • bool EndsWith<T>(T) where T : IEquatable<T>? reference
  • SpanSplitEnumerator<T> Split<T>(T) where T : IEquatable<T> reference
  • SpanSplitEnumerator<T> Split<T>(ReadOnlySpan<T>) where T : IEquatable<T> reference
  • SpanSplitEnumerator<T> SplitAny<T>(ReadOnlySpan<T>) where T : IEquatable<T> reference
  • SpanSplitEnumerator<T> SplitAny<T>(SearchValues<T>) where T : IEquatable<T> reference
  • bool StartsWith<T>(T) where T : IEquatable<T>? reference

Regex

  • ValueMatchEnumerator EnumerateMatches(ReadOnlySpan<char>) reference
  • ValueMatchEnumerator EnumerateMatches(ReadOnlySpan<char>, int) reference
  • bool IsMatch(ReadOnlySpan<char>, int) reference
  • bool IsMatch(ReadOnlySpan<char>) reference

sbyte

  • bool TryFormat(Span<char>, int, ReadOnlySpan<char>, IFormatProvider?) reference

short

  • bool TryFormat(Span<char>, int, ReadOnlySpan<char>, IFormatProvider?) reference

SortedList<TKey, TValue>

  • TKey GetKeyAtIndex<TKey, TValue>(int) reference
  • TValue GetValueAtIndex<TKey, TValue>(int) reference

Span

Span

  • bool Contains<T>(T) where T : IEquatable<T> reference

Stream

  • Task CopyToAsync(Stream, CancellationToken) reference
  • ValueTask DisposeAsync() reference
  • ValueTask<int> ReadAsync(Memory<byte>, CancellationToken) reference
  • ValueTask WriteAsync(ReadOnlyMemory<byte>, CancellationToken) reference

string

StringBuilder

  • StringBuilder Append(ReadOnlySpan<char>) reference
  • StringBuilder Append(AppendInterpolatedStringHandler) reference
  • StringBuilder Append(IFormatProvider?, AppendInterpolatedStringHandler) reference
  • StringBuilder Append(StringBuilder.AppendInterpolatedStringHandler) reference
  • StringBuilder Append(IFormatProvider?, StringBuilder.AppendInterpolatedStringHandler) reference
  • StringBuilder AppendJoin(string, string[]) reference
  • StringBuilder AppendJoin(string, Object[]) reference
  • StringBuilder AppendJoin(char, string[]) reference
  • StringBuilder AppendJoin(char, object[]) reference
  • StringBuilder AppendJoin<T>(char, T[]) reference
  • StringBuilder AppendJoin<T>(string, T[]) reference
  • StringBuilder AppendLine(AppendInterpolatedStringHandler) reference
  • StringBuilder AppendLine(IFormatProvider?, AppendInterpolatedStringHandler) reference
  • StringBuilder AppendLine(StringBuilder.AppendInterpolatedStringHandler) reference
  • StringBuilder AppendLine(IFormatProvider?, StringBuilder.AppendInterpolatedStringHandler) reference
  • void CopyTo(int, Span<char>, int) reference
  • bool Equals(ReadOnlySpan<char>) reference
  • ChunkEnumerator GetChunks() reference
  • StringBuilder Replace(ReadOnlySpan<char>, ReadOnlySpan<char>) reference
  • StringBuilder Replace(ReadOnlySpan<char>, ReadOnlySpan<char>, int, int) reference

Task

Task

  • Task<TResult> WaitAsync<TResult>(CancellationToken) reference
  • Task<TResult> WaitAsync<TResult>(TimeSpan) reference
  • Task<TResult> WaitAsync<TResult>(TimeSpan, CancellationToken) reference

TaskCompletionSource

  • void SetCanceled<T>(CancellationToken) reference

TextReader

  • ValueTask<int> ReadAsync(Memory<char>, CancellationToken) reference
  • Task<string> ReadLineAsync(CancellationToken) reference
  • Task<string> ReadToEndAsync(CancellationToken) reference

TextWriter

  • Task FlushAsync(CancellationToken) reference
  • void Write(StringBuilder?) reference
  • void Write(ReadOnlySpan<char>) reference
  • Task WriteAsync(StringBuilder?, CancellationToken) reference
  • ValueTask WriteAsync(ReadOnlyMemory<char>, CancellationToken) reference
  • void WriteLine(ReadOnlySpan<char>) reference
  • ValueTask WriteLineAsync(ReadOnlyMemory<char>, CancellationToken) reference

TimeOnly

  • bool TryFormat(Span<char>, int, ReadOnlySpan<char>, IFormatProvider?) reference

TimeSpan

  • int Microseconds() reference
  • int Nanoseconds() reference
  • bool TryFormat(Span<char>, int, ReadOnlySpan<char>, IFormatProvider?) reference

Type

  • MethodInfo? GetMethod(string, int, BindingFlags, Type[]) reference
  • bool IsAssignableFrom<T>()
  • bool IsAssignableTo<T>()
  • bool IsAssignableTo(Type?) reference
  • bool IsGenericMethodParameter() reference

uint

  • bool TryFormat(Span<char>, int, ReadOnlySpan<char>, IFormatProvider?) reference

ulong

  • bool TryFormat(Span<char>, int, ReadOnlySpan<char>, IFormatProvider?) reference

ushort

  • bool TryFormat(Span<char>, int, ReadOnlySpan<char>, IFormatProvider?) reference

XDocument

  • Task SaveAsync(XmlWriter, CancellationToken) reference
  • Task SaveAsync(Stream, SaveOptions, CancellationToken) reference
  • Task SaveAsync(TextWriter, SaveOptions, CancellationToken) reference

Static helpers

EnumPolyfill

  • TEnum[] GetValues<TEnum>() where TEnum : struct, Enum reference
  • bool IsDefined<TEnum>() where TEnum : struct, Enum reference
  • string[] GetNames<TEnum>() where TEnum : struct, Enum reference
  • TEnum Parse<TEnum>() where TEnum : struct, Enum reference
  • TEnum Parse<TEnum>(bool) where TEnum : struct, Enum reference
  • TEnum Parse<TEnum>() where TEnum : struct, Enum reference
  • TEnum Parse<TEnum>(bool) where TEnum : struct, Enum reference
  • bool TryParse<TEnum>(TEnum) where TEnum : struct, Enum reference
  • bool TryParse<TEnum>(bool, TEnum) where TEnum : struct, Enum reference

RegexPolyfill

  • bool IsMatch(string, RegexOptions, TimeSpan) reference
  • bool IsMatch(string, RegexOptions) reference
  • bool IsMatch(string) reference
  • ValueMatchEnumerator EnumerateMatches(string) reference
  • ValueMatchEnumerator EnumerateMatches(string, RegexOptions, TimeSpan) reference
  • ValueMatchEnumerator EnumerateMatches(string, RegexOptions) reference

StringPolyfill

BytePolyfill

  • bool TryParse(IFormatProvider?, byte) reference
  • bool TryParse(IFormatProvider?, byte) reference
  • bool TryParse(byte) reference
  • bool TryParse(IFormatProvider?, byte) reference
  • bool TryParse(NumberStyles, IFormatProvider?, byte) reference
  • bool TryParse(byte) reference
  • bool TryParse(NumberStyles, IFormatProvider?, byte) reference

GuidPolyfill

  • bool TryParse(IFormatProvider?, Guid) reference
  • bool TryParseExact(ReadOnlySpan<char>, Guid) reference
  • bool TryParse(IFormatProvider?, Guid) reference
  • bool TryParse(Guid) reference

DateTimePolyfill

  • bool TryParse(IFormatProvider?, DateTime) reference
  • bool TryParse(IFormatProvider?, DateTime) reference
  • bool TryParse(DateTime) reference
  • bool TryParse(IFormatProvider?, DateTimeStyles, DateTime) reference
  • bool TryParseExact(string, IFormatProvider?, DateTimeStyles, DateTime) reference
  • bool TryParseExact(ReadOnlySpan<char>, IFormatProvider?, DateTimeStyles, DateTime) reference

DateTimeOffsetPolyfill

  • bool TryParse(IFormatProvider?, DateTimeOffset) reference
  • bool TryParse(IFormatProvider?, DateTimeOffset) reference
  • bool TryParse(DateTimeOffset) reference
  • bool TryParse(IFormatProvider?, DateTimeStyles, DateTimeOffset) reference
  • bool TryParseExact(string, IFormatProvider?, DateTimeStyles, DateTimeOffset) reference
  • bool TryParseExact(ReadOnlySpan<char>, IFormatProvider?, DateTimeStyles, DateTimeOffset) reference

DoublePolyfill

  • bool TryParse(IFormatProvider?, double) reference
  • bool TryParse(IFormatProvider?, double) reference
  • bool TryParse(double) reference
  • bool TryParse(IFormatProvider?, double) reference
  • bool TryParse(NumberStyles, IFormatProvider?, double) reference
  • bool TryParse(double) reference
  • bool TryParse(NumberStyles, IFormatProvider?, double) reference

IntPolyfill

  • bool TryParse(IFormatProvider?, int) reference
  • bool TryParse(IFormatProvider?, int) reference
  • bool TryParse(int) reference
  • bool TryParse(IFormatProvider?, int) reference
  • bool TryParse(NumberStyles, IFormatProvider?, int) reference
  • bool TryParse(int) reference
  • bool TryParse(NumberStyles, IFormatProvider?, int) reference

LongPolyfill

  • bool TryParse(IFormatProvider?, int) reference
  • bool TryParse(IFormatProvider?, int) reference
  • bool TryParse(int) reference
  • bool TryParse(IFormatProvider?, int) reference
  • bool TryParse(NumberStyles, IFormatProvider?, int) reference
  • bool TryParse(int) reference
  • bool TryParse(NumberStyles, IFormatProvider?, int) reference

SBytePolyfill

  • bool TryParse(IFormatProvider?, sbyte) reference
  • bool TryParse(IFormatProvider?, sbyte) reference
  • bool TryParse(sbyte) reference
  • bool TryParse(IFormatProvider?, sbyte) reference
  • bool TryParse(NumberStyles, IFormatProvider?, sbyte) reference
  • bool TryParse(sbyte) reference
  • bool TryParse(NumberStyles, IFormatProvider?, sbyte) reference

ShortPolyfill

  • bool TryParse(IFormatProvider?, short) reference
  • bool TryParse(IFormatProvider?, short) reference
  • bool TryParse(short) reference
  • bool TryParse(IFormatProvider?, short) reference
  • bool TryParse(NumberStyles, IFormatProvider?, short) reference
  • bool TryParse(short) reference
  • bool TryParse(NumberStyles, IFormatProvider?, short) reference

UIntPolyfill

  • bool TryParse(IFormatProvider?, uint) reference
  • bool TryParse(IFormatProvider?, uint) reference
  • bool TryParse(uint) reference
  • bool TryParse(IFormatProvider?, uint) reference
  • bool TryParse(NumberStyles, IFormatProvider?, uint) reference
  • bool TryParse(uint) reference
  • bool TryParse(NumberStyles, IFormatProvider?, uint) reference

ULongPolyfill

  • bool TryParse(IFormatProvider?, ulong) reference
  • bool TryParse(IFormatProvider?, ulong) reference
  • bool TryParse(ulong) reference
  • bool TryParse(IFormatProvider?, ulong) reference
  • bool TryParse(NumberStyles, IFormatProvider?, ulong) reference
  • bool TryParse(ulong) reference
  • bool TryParse(NumberStyles, IFormatProvider?, ulong) reference

UShortPolyfill

  • bool TryParse(IFormatProvider?, ushort) reference
  • bool TryParse(IFormatProvider?, ushort) reference
  • bool TryParse(ushort) reference
  • bool TryParse(IFormatProvider?, ushort) reference
  • bool TryParse(NumberStyles, IFormatProvider?, ushort) reference
  • bool TryParse(ushort) reference
  • bool TryParse(NumberStyles, IFormatProvider?, ushort) reference

Guard

  • void FileExists()
  • void DirectoryExists()
  • void NotEmpty()
  • void NotEmpty<T>()
  • void NotEmpty<T>()
  • void NotEmpty<T>()
  • void NotEmpty<T>()
  • void NotEmpty<T>()
  • void NotEmpty<T>()
  • void NotEmpty<T>() where T : IEnumerable
  • T NotNull<T>() where T : class
  • string NotNull()
  • string NotNullOrEmpty()
  • T NotNullOrEmpty<T>() where T : IEnumerable
  • Memory<char> NotNullOrEmpty()
  • ReadOnlyMemory<char> NotNullOrEmpty()
  • string NotNullOrWhiteSpace()
  • Memory<char> NotNullOrWhiteSpace()
  • ReadOnlyMemory<char> NotNullOrWhiteSpace()
  • void NotWhiteSpace()
  • void NotWhiteSpace()
  • void NotWhiteSpace()
  • void NotWhiteSpace()
  • void NotWhiteSpace()

Lock

  • void Enter()
  • bool TryEnter()
  • bool TryEnter(TimeSpan)
  • bool TryEnter(int)
  • void Exit()
  • Scope EnterScope()

TaskCompletionSource

References

If any of the below reference are not included, the related polyfills will be disabled.

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.StartsWith('net46'))" />

System.Memory

If using Span APIs and consuming in a project that targets netstandard, netframework, or netcoreapp2*, a reference to System.Memory nuget is required.

<PackageReference Include="System.Memory"
                  Version="4.5.5"
                  Condition="$(TargetFrameworkIdentifier) == '.NETStandard' or
                             $(TargetFrameworkIdentifier) == '.NETFramework' or
                             $(TargetFramework.StartsWith('netcoreapp2'))" />

System.Threading.Tasks.Extensions

If using ValueTask APIs and consuming in a project that target netframework, netstandard2, or netcoreapp2, a reference to System.Threading.Tasks.Extensions nuget is required.

<PackageReference Include="System.Threading.Tasks.Extensions"
                  Version="4.5.4"
                  Condition="$(TargetFramework) == 'netstandard2.0' or
                             $(TargetFramework) == 'netcoreapp2.0' or
                             $(TargetFrameworkIdentifier) == '.NETFramework'" />

Nullability

Example target class

Given the following class

class NullabilityTarget
{
    public string? StringField;
    public string?[] ArrayField;
    public Dictionary<string, object?> GenericField;
}

snippet source | anchor

NullabilityInfoContext

[Test]
public void Test()
{
    var type = typeof(NullabilityTarget);
    var arrayField = type.GetField("ArrayField")!;
    var genericField = type.GetField("GenericField")!;

    var context = new NullabilityInfoContext();

    var arrayInfo = context.Create(arrayField);

    Assert.AreEqual(NullabilityState.NotNull, arrayInfo.ReadState);
    Assert.AreEqual(NullabilityState.Nullable, arrayInfo.ElementType!.ReadState);

    var genericInfo = context.Create(genericField);

    Assert.AreEqual(NullabilityState.NotNull, genericInfo.ReadState);
    Assert.AreEqual(NullabilityState.NotNull, genericInfo.GenericTypeArguments[0].ReadState);
    Assert.AreEqual(NullabilityState.Nullable, genericInfo.GenericTypeArguments[1].ReadState);
}

snippet source | anchor

NullabilityInfoExtensions

Enable by adding and MSBuild property PolyNullability

<PropertyGroup>
  ...
  <PolyNullability>true</PolyNullability>
</PropertyGroup>

NullabilityInfoExtensions provides static and thread safe wrapper around NullabilityInfoContext. It adds three extension methods to each of ParameterInfo, PropertyInfo, EventInfo, and FieldInfo.

  • GetNullabilityInfo: returns the NullabilityInfo for the target info.
  • GetNullability: returns the NullabilityState for the state (NullabilityInfo.ReadState or NullabilityInfo.WriteState depending on which has more info) of target info.
  • IsNullable: given the state (NullabilityInfo.ReadState or NullabilityInfo.WriteState depending on which has more info) of the info:
    • Returns true if state is NullabilityState.Nullable.
    • Returns false if state is NullabilityState.NotNull.
    • Throws an exception if state is NullabilityState.Unknown.

Guard

Enable by adding and MSBuild property PolyGuard

<PropertyGroup>
  ...
  <PolyGuard>true</PolyGuard>
</PropertyGroup>

Guard is designed to be a an alternative to the ArgumentException.ThrowIf* APIs added in net7.

  • ArgumentException.ThrowIfNullOrEmpty reference
  • ArgumentException.ThrowIfNullOrWhiteSpace reference
  • ArgumentNullException.ThrowIfNull reference

With the equivalent Guard APIs:

  • Guard.NotNullOrEmpty
  • Guard.NotNullOrWhiteSpace
  • Guard.NotNull

Polyfills.Guard provides the following APIs:

Guard

  • void DirectoryExists(String)
  • void FileExists(String)
  • void NotEmpty(String)
  • void NotEmpty<T>(ReadOnlySpan<T>)
  • void NotEmpty<T>(Span<T>)
  • void NotEmpty<T>(Nullable<Memory<T>>)
  • void NotEmpty<T>(Memory<T>)
  • void NotEmpty<T>(Nullable<ReadOnlyMemory<T>>)
  • void NotEmpty<T>(ReadOnlyMemory<T>)
  • void NotEmpty<T>(T) where T : Collections.IEnumerable
  • T NotNull<T>(T)
  • String NotNull(String)
  • String NotNullOrEmpty(String)
  • T NotNullOrEmpty<T>(T) where T : Collections.IEnumerable
  • Memory<Char> NotNullOrEmpty(Nullable<Memory<Char>>)
  • ReadOnlyMemory<Char> NotNullOrEmpty(Nullable<ReadOnlyMemory<Char>>)
  • String NotNullOrWhiteSpace(String)
  • Memory<Char> NotNullOrWhiteSpace(Nullable<Memory<Char>>)
  • ReadOnlyMemory<Char> NotNullOrWhiteSpace(Nullable<ReadOnlyMemory<Char>>)
  • void NotWhiteSpace(String)
  • void NotWhiteSpace(ReadOnlySpan<Char>)
  • void NotWhiteSpace(Nullable<Memory<Char>>)
  • void NotWhiteSpace(Nullable<ReadOnlyMemory<Char>>)
  • void NotWhiteSpace(Span<Char>)

Alternatives

PolyShim

https://github.com/Tyrrrz/PolyShim

PolySharp

https://github.com/Sergio0694/PolySharp

Theraot.Core

https://github.com/theraot/Theraot

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.

Notes

Icon

Crack designed by Adrien Coquet from The Noun Project.

About

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

Resources

License

Code of conduct

Stars

Watchers

Forks

Sponsor this project

 

Languages