Polyfill
Polyfill copied to clipboard
Source only package that exposes newer .net and C# features to older runtimes.
Polyfill
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,net481netcoreapp2.0,netcoreapp2.1,netcoreapp3.0,netcoreapp3.1net5.0,net6.0,net7.0,net8.0,net9.0
API count: 288
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": "7.0.306",
"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 InitExample
{
public int Member { get; init; }
}
snippet source | anchor
Nullable attributes
- AllowNullAttribute
- DisallowNullAttribute
- DoesNotReturnAttribute
- DoesNotReturnIfAttribute
- MaybeNullAttribute
- MaybeNullWhenAttribute
- MemberNotNullAttribute
- MemberNotNullWhenAttribute
- NotNullAttribute
- NotNullIfNotNullAttribute
- NotNullWhenAttribute
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.
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
UnscopedRefAttribute
Reference: Low Level Struct Improvements
#if !NET7_0_OR_GREATER
using System.Diagnostics.CodeAnalysis;
struct UnscopedRefUsage
{
int field;
[UnscopedRef] ref int Prop1 => ref field;
}
#endif
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
InterpolatedStringHandler
- AppendInterpolatedStringHandler
- DefaultInterpolatedStringHandler
- InterpolatedStringHandlerAttribute
- InterpolatedStringHandlerArgumentAttribute
- ISpanFormattable
References: String Interpolation in C# 10 and .NET 6, Write a custom string interpolation handler
StringSyntaxAttribute
Reference: .NET 7 - The StringSyntaxAttribute
Trimming annotation attributes
- DynamicallyAccessedMembersAttribute
- DynamicDependencyAttribute
- RequiresUnreferencedCodeAttribute
- RequiresDynamicCodeAttribute
- UnconditionalSuppressMessageAttribute
Reference: Prepare .NET libraries for trimming
Platform compatibility
- ObsoletedOSPlatformAttribute
- SupportedOSPlatformAttribute
- SupportedOSPlatformGuardAttribute
- TargetPlatformAttribute
- UnsupportedOSPlatformAttribute
- UnsupportedOSPlatformGuardAttribute
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
AppendInterpolatedStringHandlerparameter are not extensions because the compiler prefers to use the overload withstringparameter instead.
Extension methods
Boolean
Boolean TryFormat(Span<Char>, Int32&)reference
Byte
Boolean TryFormat(Span<Char>, Int32&, ReadOnlySpan<Char>, IFormatProvider)reference
Collections.Concurrent.ConcurrentDictionary<TKey,TValue>
TValue GetOrAdd<TKey, TValue, TArg>(TKey, Func<TKey,TArg,TValue>, TArg)reference
Dictionary<TKey,TValue>
Boolean Remove<TKey, TValue>(TKey, TValue&)reference
HashSet<T>
Boolean TryGetValue<T>(T, T&)reference
IDictionary<TKey,TValue>
Collections.ObjectModel.ReadOnlyDictionary<TKey,TValue> AsReadOnly<TKey, TValue>()reference
IEnumerable<TFirst>
IEnumerable<ValueTuple<TFirst,TSecond,TThird>> Zip<TFirst, TSecond, TThird>(IEnumerable<TSecond>, IEnumerable<TThird>)referenceIEnumerable<ValueTuple<TFirst,TSecond>> Zip<TFirst, TSecond>(IEnumerable<TSecond>)reference
IEnumerable<TSource>
IEnumerable<KeyValuePair<TKey,TAccumulate>> AggregateBy<TSource, TKey, TAccumulate>(Func<TSource,TKey>, TAccumulate, Func<TAccumulate,TSource,TAccumulate>, IEqualityComparer<TKey>)referenceIEnumerable<KeyValuePair<TKey,TAccumulate>> AggregateBy<TSource, TKey, TAccumulate>(Func<TSource,TKey>, Func<TKey,TAccumulate>, Func<TAccumulate,TSource,TAccumulate>, IEqualityComparer<TKey>)referenceIEnumerable<TSource> Append<TSource>(TSource)referenceIEnumerable<TSource[]> Chunk<TSource>(Int32)referenceIEnumerable<KeyValuePair<TKey,Int32>> CountBy<TSource, TKey>(Func<TSource,TKey>, IEqualityComparer<TKey>)referenceIEnumerable<TSource> DistinctBy<TSource, TKey>(Func<TSource,TKey>)referenceIEnumerable<TSource> DistinctBy<TSource, TKey>(Func<TSource,TKey>, IEqualityComparer<TKey>)referenceTSource ElementAt<TSource>(Index)referenceTSource ElementAtOrDefault<TSource>(Index)referenceIEnumerable<TSource> Except<TSource>(TSource)referenceIEnumerable<TSource> Except<TSource>(TSource[])referenceIEnumerable<TSource> Except<TSource>(TSource, IEqualityComparer<TSource>)referenceIEnumerable<TSource> Except<TSource>(IEqualityComparer<TSource>, TSource[])referenceIEnumerable<TSource> ExceptBy<TSource, TKey>(IEnumerable<TKey>, Func<TSource,TKey>)referenceIEnumerable<TSource> ExceptBy<TSource, TKey>(IEnumerable<TKey>, Func<TSource,TKey>, IEqualityComparer<TKey>)referenceTSource FirstOrDefault<TSource>(Func<TSource,Boolean>, TSource)referenceTSource FirstOrDefault<TSource>(TSource)referenceIEnumerable<ValueTuple<Int32,TSource>> Index<TSource>()referenceTSource LastOrDefault<TSource>(TSource)referenceTSource LastOrDefault<TSource>(Func<TSource,Boolean>, TSource)referenceTSource MaxBy<TSource, TKey>(Func<TSource,TKey>)referenceTSource MaxBy<TSource, TKey>(Func<TSource,TKey>, IComparer<TKey>)referenceTSource MinBy<TSource, TKey>(Func<TSource,TKey>)referenceTSource MinBy<TSource, TKey>(Func<TSource,TKey>, IComparer<TKey>)referenceTSource SingleOrDefault<TSource>(Func<TSource,Boolean>, TSource)referenceTSource SingleOrDefault<TSource>(TSource)referenceIEnumerable<TSource> SkipLast<TSource>(Int32)referenceHashSet<TSource> ToHashSet<TSource>(IEqualityComparer<TSource>)referenceBoolean TryGetNonEnumeratedCount<TSource>(Int32&)reference
IList<T>
Collections.ObjectModel.ReadOnlyCollection<T> AsReadOnly<T>()reference
IReadOnlyDictionary<TKey,TValue>
TValue GetValueOrDefault<TKey, TValue>(TKey)referenceTValue GetValueOrDefault<TKey, TValue>(TKey, TValue)reference
KeyValuePair<TKey,TValue>
Void Deconstruct<TKey, TValue>(TKey&, TValue&)reference
List<T>
Void AddRange<T>(ReadOnlySpan<T>)referenceVoid CopyTo<T>(Span<T>)referenceVoid InsertRange<T>(Int32, ReadOnlySpan<T>)reference
SortedList<TKey,TValue>
TKey GetKeyAtIndex<TKey, TValue>(Int32)referenceTValue GetValueAtIndex<TKey, TValue>(Int32)reference
DateOnly
Boolean TryFormat(Span<Char>, Int32&, ReadOnlySpan<Char>, IFormatProvider)reference
DateTime
DateTime AddMicroseconds(Double)referenceInt32 Microsecond()referenceInt32 Nanosecond()referenceBoolean TryFormat(Span<Char>, Int32&, ReadOnlySpan<Char>, IFormatProvider)reference
DateTimeOffset
DateTimeOffset AddMicroseconds(Double)referenceInt32 Microsecond()referenceInt32 Nanosecond()referenceBoolean TryFormat(Span<Char>, Int32&, ReadOnlySpan<Char>, IFormatProvider)reference
Decimal
Boolean TryFormat(Span<Char>, Int32&, ReadOnlySpan<Char>, IFormatProvider)reference
Process
Task WaitForExitAsync(CancellationToken)reference
Double
Boolean TryFormat(Span<Char>, Int32&, ReadOnlySpan<Char>, IFormatProvider)reference
Guid
Boolean TryFormat(Span<Char>, Int32&, ReadOnlySpan<Char>)reference
Int16
Boolean TryFormat(Span<Char>, Int32&, ReadOnlySpan<Char>, IFormatProvider)reference
Int32
Boolean TryFormat(Span<Char>, Int32&, ReadOnlySpan<Char>, IFormatProvider)reference
Int64
Boolean TryFormat(Span<Char>, Int32&, ReadOnlySpan<Char>, IFormatProvider)reference
Stream
Task CopyToAsync(Stream, CancellationToken)referenceValueTask<Int32> ReadAsync(Memory<Byte>, CancellationToken)referenceValueTask WriteAsync(ReadOnlyMemory<Byte>, CancellationToken)reference
TextReader
ValueTask<Int32> ReadAsync(Memory<Char>, CancellationToken)referenceTask<String> ReadLineAsync(CancellationToken)referenceTask<String> ReadToEndAsync(CancellationToken)reference
TextWriter
Void Write(ReadOnlySpan<Char>)referenceValueTask WriteAsync(ReadOnlyMemory<Char>, CancellationToken)referenceVoid WriteLine(ReadOnlySpan<Char>)referenceValueTask WriteLineAsync(ReadOnlyMemory<Char>, CancellationToken)reference
HttpClient
Task<Byte[]> GetByteArrayAsync(String, CancellationToken)referenceTask<Byte[]> GetByteArrayAsync(Uri, CancellationToken)referenceTask<Stream> GetStreamAsync(String, CancellationToken)referenceTask<Stream> GetStreamAsync(Uri, CancellationToken)referenceTask<String> GetStringAsync(String, CancellationToken)referenceTask<String> GetStringAsync(Uri, CancellationToken)reference
HttpContent
Task<Byte[]> ReadAsByteArrayAsync(CancellationToken)referenceTask<Stream> ReadAsStreamAsync(CancellationToken)referenceTask<String> ReadAsStringAsync(CancellationToken)reference
ReadOnlySpan<Char>
Boolean EndsWith(String, StringComparison)referenceSpanLineEnumerator EnumerateLines()referenceBoolean SequenceEqual(String)referenceBoolean StartsWith(String, StringComparison)reference
ReadOnlySpan<T>
Boolean Contains<T>(T)reference
Reflection.EventInfo
Reflection.NullabilityState GetNullability()Reflection.NullabilityInfo GetNullabilityInfo()Boolean IsNullable()
Reflection.FieldInfo
Reflection.NullabilityState GetNullability()Reflection.NullabilityInfo GetNullabilityInfo()Boolean IsNullable()
Reflection.MemberInfo
Reflection.NullabilityState GetNullability()Reflection.NullabilityInfo GetNullabilityInfo()Boolean HasSameMetadataDefinitionAs(Reflection.MemberInfo)referenceBoolean IsNullable()
Reflection.ParameterInfo
Reflection.NullabilityState GetNullability()Reflection.NullabilityInfo GetNullabilityInfo()Boolean IsNullable()
Reflection.PropertyInfo
Reflection.NullabilityState GetNullability()Reflection.NullabilityInfo GetNullabilityInfo()Boolean IsNullable()
SByte
Boolean TryFormat(Span<Char>, Int32&, ReadOnlySpan<Char>, IFormatProvider)reference
Single
Boolean TryFormat(Span<Char>, Int32&, ReadOnlySpan<Char>, IFormatProvider)reference
Span<Char>
Boolean EndsWith(String)referenceSpanLineEnumerator EnumerateLines()referenceBoolean SequenceEqual(String)referenceBoolean StartsWith(String)referenceSpan<Char> TrimEnd()referenceSpan<Char> TrimStart()reference
Span<T>
Boolean Contains<T>(T)reference
String
Boolean Contains(String, StringComparison)referenceBoolean Contains(Char)referenceVoid CopyTo(Span<Char>)referenceBoolean EndsWith(Char)referenceInt32 GetHashCode(StringComparison)referenceString[] Split(Char, StringSplitOptions)referenceString[] Split(Char, Int32, StringSplitOptions)referenceBoolean StartsWith(Char)referenceBoolean TryCopyTo(Span<Char>)reference
Regex
ValueMatchEnumerator EnumerateMatches(ReadOnlySpan<Char>)referenceValueMatchEnumerator EnumerateMatches(ReadOnlySpan<Char>, Int32)referenceBoolean IsMatch(ReadOnlySpan<Char>, Int32)referenceBoolean IsMatch(ReadOnlySpan<Char>)reference
StringBuilder
StringBuilder Append(ReadOnlySpan<Char>)referenceStringBuilder Append(StringBuilder, AppendInterpolatedStringHandler&)referenceStringBuilder Append(StringBuilder, IFormatProvider, AppendInterpolatedStringHandler&)referenceStringBuilder Append(StringBuilder, StringBuilder/AppendInterpolatedStringHandler&)referenceStringBuilder Append(StringBuilder, IFormatProvider, StringBuilder/AppendInterpolatedStringHandler&)referenceStringBuilder AppendJoin(String, String[])referenceStringBuilder AppendJoin(String, Object[])referenceStringBuilder AppendJoin(Char, String[])referenceStringBuilder AppendJoin(Char, Object[])referenceStringBuilder AppendJoin<T>(Char, T[])referenceStringBuilder AppendJoin<T>(String, T[])referenceStringBuilder AppendLine(StringBuilder, AppendInterpolatedStringHandler&)referenceStringBuilder AppendLine(StringBuilder, IFormatProvider, AppendInterpolatedStringHandler&)referenceStringBuilder AppendLine(StringBuilder, StringBuilder/AppendInterpolatedStringHandler&)referenceStringBuilder AppendLine(StringBuilder, IFormatProvider, StringBuilder/AppendInterpolatedStringHandler&)referenceVoid CopyTo(Int32, Span<Char>, Int32)referenceBoolean Equals(ReadOnlySpan<Char>)referencePolyfill/ChunkEnumerator GetChunks()referenceStringBuilder Replace(ReadOnlySpan<Char>, ReadOnlySpan<Char>)referenceStringBuilder Replace(ReadOnlySpan<Char>, ReadOnlySpan<Char>, Int32, Int32)[reference](https://learn.microsoft.com/en-us/dotnet/api/system.text.stringbuilder.replace#system-text-stringbuilder-replace(system-readonlyspan((system-char))-system-readonlyspan((system-char))-system-int32-system-int32)
CancellationToken
CancellationTokenRegistration Register(Action<Object,CancellationToken>, Object)referenceCancellationTokenRegistration UnsafeRegister(Action<Object>, Object)referenceCancellationTokenRegistration UnsafeRegister(Action<Object,CancellationToken>, Object)reference
CancellationTokenSource
Task CancelAsync()reference
Task
Task WaitAsync(CancellationToken)referenceTask WaitAsync(TimeSpan)referenceTask WaitAsync(TimeSpan, CancellationToken)reference
Task<TResult>
Task<TResult> WaitAsync<TResult>(CancellationToken)referenceTask<TResult> WaitAsync<TResult>(TimeSpan)referenceTask<TResult> WaitAsync<TResult>(TimeSpan, CancellationToken)reference
TimeOnly
Boolean TryFormat(Span<Char>, Int32&, ReadOnlySpan<Char>, IFormatProvider)reference
TimeSpan
Int32 Microseconds()referenceInt32 Nanoseconds()referenceBoolean TryFormat(Span<Char>, Int32&, ReadOnlySpan<Char>, IFormatProvider)reference
Type
Boolean IsAssignableFrom<T>()Boolean IsAssignableTo<T>()Boolean IsAssignableTo(Type)referenceBoolean IsGenericMethodParameter()reference
UInt16
Boolean TryFormat(Span<Char>, Int32&, ReadOnlySpan<Char>, IFormatProvider)reference
UInt32
Boolean TryFormat(Span<Char>, Int32&, ReadOnlySpan<Char>, IFormatProvider)reference
UInt64
Boolean TryFormat(Span<Char>, Int32&, ReadOnlySpan<Char>, IFormatProvider)reference
Static helpers
EnumPolyfill
RegexPolyfill
ValueMatchEnumerator EnumerateMatches(ReadOnlySpan<Char>, String)referenceValueMatchEnumerator EnumerateMatches(ReadOnlySpan<Char>, String, RegexOptions, TimeSpan)referenceValueMatchEnumerator EnumerateMatches(ReadOnlySpan<Char>, String, RegexOptions)referenceRegex/ValueMatchEnumerator EnumerateMatches(ReadOnlySpan<Char>, String)referenceRegex/ValueMatchEnumerator EnumerateMatches(ReadOnlySpan<Char>, String, RegexOptions, TimeSpan)referenceRegex/ValueMatchEnumerator EnumerateMatches(ReadOnlySpan<Char>, String, RegexOptions)referenceBoolean IsMatch(ReadOnlySpan<Char>, String, RegexOptions, TimeSpan)referenceBoolean IsMatch(ReadOnlySpan<Char>, String, RegexOptions)referenceBoolean IsMatch(ReadOnlySpan<Char>, String)reference
StringPolyfill
String Join(Char, String[])referenceString Join(Char, Object[])referenceString Join(Char, String[], Int32, Int32)referenceString Join<T>(Char, IEnumerable<T>)reference
BytePolyfill
Boolean TryParse(String, IFormatProvider, Byte&)referenceBoolean TryParse(ReadOnlySpan<Byte>, IFormatProvider, Byte&)referenceBoolean TryParse(ReadOnlySpan<Char>, Byte&)referenceBoolean TryParse(ReadOnlySpan<Char>, IFormatProvider, Byte&)referenceBoolean TryParse(ReadOnlySpan<Byte>, Globalization.NumberStyles, IFormatProvider, Byte&)referenceBoolean TryParse(ReadOnlySpan<Byte>, Byte&)referenceBoolean TryParse(ReadOnlySpan<Char>, Globalization.NumberStyles, IFormatProvider, Byte&)reference
DoublePolyfill
Boolean TryParse(String, IFormatProvider, Double&)referenceBoolean TryParse(ReadOnlySpan<Byte>, IFormatProvider, Double&)referenceBoolean TryParse(ReadOnlySpan<Char>, Double&)referenceBoolean TryParse(ReadOnlySpan<Char>, IFormatProvider, Double&)referenceBoolean TryParse(ReadOnlySpan<Byte>, Globalization.NumberStyles, IFormatProvider, Double&)referenceBoolean TryParse(ReadOnlySpan<Byte>, Double&)referenceBoolean TryParse(ReadOnlySpan<Char>, Globalization.NumberStyles, IFormatProvider, Double&)reference
IntPolyfill
Boolean TryParse(String, IFormatProvider, Int32&)referenceBoolean TryParse(ReadOnlySpan<Byte>, IFormatProvider, Int32&)referenceBoolean TryParse(ReadOnlySpan<Char>, Int32&)referenceBoolean TryParse(ReadOnlySpan<Char>, IFormatProvider, Int32&)referenceBoolean TryParse(ReadOnlySpan<Byte>, Globalization.NumberStyles, IFormatProvider, Int32&)referenceBoolean TryParse(ReadOnlySpan<Byte>, Int32&)referenceBoolean TryParse(ReadOnlySpan<Char>, Globalization.NumberStyles, IFormatProvider, Int32&)reference
LongPolyfill
Boolean TryParse(String, IFormatProvider, Int32&)referenceBoolean TryParse(ReadOnlySpan<Byte>, IFormatProvider, Int32&)referenceBoolean TryParse(ReadOnlySpan<Char>, Int32&)referenceBoolean TryParse(ReadOnlySpan<Char>, IFormatProvider, Int32&)referenceBoolean TryParse(ReadOnlySpan<Byte>, Globalization.NumberStyles, IFormatProvider, Int32&)referenceBoolean TryParse(ReadOnlySpan<Byte>, Int32&)referenceBoolean TryParse(ReadOnlySpan<Char>, Globalization.NumberStyles, IFormatProvider, Int32&)reference
SBytePolyfill
Boolean TryParse(String, IFormatProvider, SByte&)referenceBoolean TryParse(ReadOnlySpan<Byte>, IFormatProvider, SByte&)referenceBoolean TryParse(ReadOnlySpan<Char>, SByte&)referenceBoolean TryParse(ReadOnlySpan<Char>, IFormatProvider, SByte&)referenceBoolean TryParse(ReadOnlySpan<Byte>, Globalization.NumberStyles, IFormatProvider, SByte&)referenceBoolean TryParse(ReadOnlySpan<Byte>, SByte&)referenceBoolean TryParse(ReadOnlySpan<Char>, Globalization.NumberStyles, IFormatProvider, SByte&)reference
ShortPolyfill
Boolean TryParse(String, IFormatProvider, Int16&)referenceBoolean TryParse(ReadOnlySpan<Byte>, IFormatProvider, Int16&)referenceBoolean TryParse(ReadOnlySpan<Char>, Int16&)referenceBoolean TryParse(ReadOnlySpan<Char>, IFormatProvider, Int16&)referenceBoolean TryParse(ReadOnlySpan<Byte>, Globalization.NumberStyles, IFormatProvider, Int16&)referenceBoolean TryParse(ReadOnlySpan<Byte>, Int16&)referenceBoolean TryParse(ReadOnlySpan<Char>, Globalization.NumberStyles, IFormatProvider, Int16&)reference
UIntPolyfill
Boolean TryParse(String, IFormatProvider, UInt32&)referenceBoolean TryParse(ReadOnlySpan<Byte>, IFormatProvider, UInt32&)referenceBoolean TryParse(ReadOnlySpan<Char>, UInt32&)referenceBoolean TryParse(ReadOnlySpan<Char>, IFormatProvider, UInt32&)referenceBoolean TryParse(ReadOnlySpan<Byte>, Globalization.NumberStyles, IFormatProvider, UInt32&)referenceBoolean TryParse(ReadOnlySpan<Byte>, UInt32&)referenceBoolean TryParse(ReadOnlySpan<Char>, Globalization.NumberStyles, IFormatProvider, UInt32&)reference
ULongPolyfill
Boolean TryParse(String, IFormatProvider, UInt64&)referenceBoolean TryParse(ReadOnlySpan<Byte>, IFormatProvider, UInt64&)referenceBoolean TryParse(ReadOnlySpan<Char>, UInt64&)referenceBoolean TryParse(ReadOnlySpan<Char>, IFormatProvider, UInt64&)referenceBoolean TryParse(ReadOnlySpan<Byte>, Globalization.NumberStyles, IFormatProvider, UInt64&)referenceBoolean TryParse(ReadOnlySpan<Byte>, UInt64&)referenceBoolean TryParse(ReadOnlySpan<Char>, Globalization.NumberStyles, IFormatProvider, UInt64&)reference
UShortPolyfill
Boolean TryParse(String, IFormatProvider, UInt16&)referenceBoolean TryParse(ReadOnlySpan<Byte>, IFormatProvider, UInt16&)referenceBoolean TryParse(ReadOnlySpan<Char>, UInt16&)referenceBoolean TryParse(ReadOnlySpan<Char>, IFormatProvider, UInt16&)referenceBoolean TryParse(ReadOnlySpan<Byte>, Globalization.NumberStyles, IFormatProvider, UInt16&)referenceBoolean TryParse(ReadOnlySpan<Byte>, UInt16&)referenceBoolean TryParse(ReadOnlySpan<Char>, Globalization.NumberStyles, IFormatProvider, UInt16&)reference
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 theNullabilityInfofor the target info.GetNullability: returns theNullabilityStatefor the state (NullabilityInfo.ReadStateorNullabilityInfo.WriteStatedepending on which has more info) of target info.IsNullable: given the state (NullabilityInfo.ReadStateorNullabilityInfo.WriteStatedepending 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.
- Returns true if state is
Alternatives
PolyShim
https://github.com/Tyrrrz/PolyShim
PolySharp
https://github.com/Sergio0694/PolySharp
Theraot.Core
https://github.com/theraot/Theraot
Combination of
- https://github.com/manuelroemer/Nullable
- https://github.com/bgrainger/IndexRange
- https://github.com/manuelroemer/IsExternalInit
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.