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: 292
See Milestones for release notes.
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"
https://nuget.org/packages/Polyfill/
This project uses features from the current stable SDK and C# language. As such consuming projects should target those:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<LangVersion>latest</LangVersion>
{
"sdk": {
"version": "8.0.301",
"rollForward": "latestFeature"
}
}
The default type visibility for all polyfills is internal
. This means it can be consumed in multiple projects and types will not conflict.
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.
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.
Reference: Module Initializers
static bool InitCalled;
[Test]
public void ModuleInitTest() =>
Assert.True(InitCalled);
[ModuleInitializer]
public static void ModuleInit() =>
InitCalled = true;
Reference: init (C# Reference)
class InitExample
{
public int Member { get; init; }
}
- AllowNullAttribute
- DisallowNullAttribute
- DoesNotReturnAttribute
- DoesNotReturnIfAttribute
- MaybeNullAttribute
- MaybeNullWhenAttribute
- MemberNotNullAttribute
- MemberNotNullWhenAttribute
- NotNullAttribute
- NotNullIfNotNullAttribute
- NotNullWhenAttribute
Reference: Nullable reference types
Reference: C# required modifier
public class Person
{
public Person() { }
[SetsRequiredMembers]
public Person(string name) =>
Name = name;
public required string Name { get; init; }
}
Indicates that compiler support for a particular feature is required for the location where this attribute is applied.
Can be used to make types compatible with collection expressions
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]);
}
}
}
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);
}
}
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
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);
}
}
- AppendInterpolatedStringHandler
- DefaultInterpolatedStringHandler
- InterpolatedStringHandlerAttribute
- InterpolatedStringHandlerArgumentAttribute
- ISpanFormattable
References: String Interpolation in C# 10 and .NET 6, Write a custom string interpolation handler
Reference: .NET 7 - The StringSyntaxAttribute
- DynamicallyAccessedMembersAttribute
- DynamicDependencyAttribute
- RequiresUnreferencedCodeAttribute
- RequiresDynamicCodeAttribute
- UnconditionalSuppressMessageAttribute
Reference: Prepare .NET libraries for trimming
- ObsoletedOSPlatformAttribute
- SupportedOSPlatformAttribute
- SupportedOSPlatformGuardAttribute
- TargetPlatformAttribute
- UnsupportedOSPlatformAttribute
- UnsupportedOSPlatformGuardAttribute
Reference: Platform compatibility analyzer
StackTraceHiddenAttribute
Reference: C# – Hide a method from the stack trace
Reference: Improvements in native code interop in .NET 5.0
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.
Boolean TryFormat(Span<Char>, Int32&)
reference
Boolean TryFormat(Span<Char>, Int32&, ReadOnlySpan<Char>, IFormatProvider)
reference
TValue GetOrAdd<TKey, TValue, TArg>(TKey, Func<TKey,TArg,TValue>, TArg)
reference
Boolean Remove<TKey, TValue>(TKey, TValue&)
reference
Boolean TryGetValue<T>(T, T&)
reference
Collections.ObjectModel.ReadOnlyDictionary<TKey,TValue> AsReadOnly<TKey, TValue>()
reference
IEnumerable<ValueTuple<TFirst,TSecond,TThird>> Zip<TFirst, TSecond, TThird>(IEnumerable<TSecond>, IEnumerable<TThird>)
referenceIEnumerable<ValueTuple<TFirst,TSecond>> Zip<TFirst, TSecond>(IEnumerable<TSecond>)
reference
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
Collections.ObjectModel.ReadOnlyCollection<T> AsReadOnly<T>()
reference
TValue GetValueOrDefault<TKey, TValue>(TKey)
referenceTValue GetValueOrDefault<TKey, TValue>(TKey, TValue)
reference
Void Deconstruct<TKey, TValue>(TKey&, TValue&)
reference
Void AddRange<T>(ReadOnlySpan<T>)
referenceVoid CopyTo<T>(Span<T>)
referenceVoid InsertRange<T>(Int32, ReadOnlySpan<T>)
reference
TKey GetKeyAtIndex<TKey, TValue>(Int32)
referenceTValue GetValueAtIndex<TKey, TValue>(Int32)
reference
Boolean TryFormat(Span<Char>, Int32&, ReadOnlySpan<Char>, IFormatProvider)
reference
DateTime AddMicroseconds(Double)
referenceInt32 Microsecond()
referenceInt32 Nanosecond()
referenceBoolean TryFormat(Span<Char>, Int32&, ReadOnlySpan<Char>, IFormatProvider)
reference
DateTimeOffset AddMicroseconds(Double)
referenceInt32 Microsecond()
referenceInt32 Nanosecond()
referenceBoolean TryFormat(Span<Char>, Int32&, ReadOnlySpan<Char>, IFormatProvider)
reference
Boolean TryFormat(Span<Char>, Int32&, ReadOnlySpan<Char>, IFormatProvider)
reference
Task WaitForExitAsync(CancellationToken)
reference
Boolean TryFormat(Span<Char>, Int32&, ReadOnlySpan<Char>, IFormatProvider)
reference
Boolean TryFormat(Span<Char>, Int32&, ReadOnlySpan<Char>)
reference
Boolean TryFormat(Span<Char>, Int32&, ReadOnlySpan<Char>, IFormatProvider)
reference
Boolean TryFormat(Span<Char>, Int32&, ReadOnlySpan<Char>, IFormatProvider)
reference
Boolean TryFormat(Span<Char>, Int32&, ReadOnlySpan<Char>, IFormatProvider)
reference
Task CopyToAsync(Stream, CancellationToken)
referenceValueTask<Int32> ReadAsync(Memory<Byte>, CancellationToken)
referenceValueTask WriteAsync(ReadOnlyMemory<Byte>, CancellationToken)
reference
ValueTask<Int32> ReadAsync(Memory<Char>, CancellationToken)
referenceTask<String> ReadLineAsync(CancellationToken)
referenceTask<String> ReadToEndAsync(CancellationToken)
reference
Void Write(ReadOnlySpan<Char>)
referenceValueTask WriteAsync(ReadOnlyMemory<Char>, CancellationToken)
referenceVoid WriteLine(ReadOnlySpan<Char>)
referenceValueTask WriteLineAsync(ReadOnlyMemory<Char>, CancellationToken)
reference
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
Task<Byte[]> ReadAsByteArrayAsync(CancellationToken)
referenceTask<Stream> ReadAsStreamAsync(CancellationToken)
referenceTask<String> ReadAsStringAsync(CancellationToken)
reference
Boolean EndsWith(String, StringComparison)
referenceSpanLineEnumerator EnumerateLines()
referenceBoolean SequenceEqual(String)
referenceBoolean StartsWith(String, StringComparison)
reference
Boolean Contains<T>(T)
reference
Reflection.NullabilityState GetNullability()
Reflection.NullabilityInfo GetNullabilityInfo()
Boolean IsNullable()
Reflection.NullabilityState GetNullability()
Reflection.NullabilityInfo GetNullabilityInfo()
Boolean IsNullable()
Reflection.NullabilityState GetNullability()
Reflection.NullabilityInfo GetNullabilityInfo()
Boolean HasSameMetadataDefinitionAs(Reflection.MemberInfo)
referenceBoolean IsNullable()
Reflection.NullabilityState GetNullability()
Reflection.NullabilityInfo GetNullabilityInfo()
Boolean IsNullable()
Reflection.NullabilityState GetNullability()
Reflection.NullabilityInfo GetNullabilityInfo()
Boolean IsNullable()
Boolean TryFormat(Span<Char>, Int32&, ReadOnlySpan<Char>, IFormatProvider)
reference
Boolean TryFormat(Span<Char>, Int32&, ReadOnlySpan<Char>, IFormatProvider)
reference
Boolean EndsWith(String)
referenceSpanLineEnumerator EnumerateLines()
referenceBoolean SequenceEqual(String)
referenceBoolean StartsWith(String)
referenceSpan<Char> TrimEnd()
referenceSpan<Char> TrimStart()
reference
Boolean Contains<T>(T)
reference
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
ValueMatchEnumerator EnumerateMatches(ReadOnlySpan<Char>)
referenceValueMatchEnumerator EnumerateMatches(ReadOnlySpan<Char>, Int32)
referenceBoolean IsMatch(ReadOnlySpan<Char>, Int32)
referenceBoolean IsMatch(ReadOnlySpan<Char>)
reference
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)
CancellationTokenRegistration Register(Action<Object,CancellationToken>, Object)
referenceCancellationTokenRegistration UnsafeRegister(Action<Object>, Object)
referenceCancellationTokenRegistration UnsafeRegister(Action<Object,CancellationToken>, Object)
reference
Task CancelAsync()
reference
Task WaitAsync(CancellationToken)
referenceTask WaitAsync(TimeSpan)
referenceTask WaitAsync(TimeSpan, CancellationToken)
reference
Task<TResult> WaitAsync<TResult>(CancellationToken)
referenceTask<TResult> WaitAsync<TResult>(TimeSpan)
referenceTask<TResult> WaitAsync<TResult>(TimeSpan, CancellationToken)
reference
Boolean TryFormat(Span<Char>, Int32&, ReadOnlySpan<Char>, IFormatProvider)
reference
Int32 Microseconds()
referenceInt32 Nanoseconds()
referenceBoolean TryFormat(Span<Char>, Int32&, ReadOnlySpan<Char>, IFormatProvider)
reference
Boolean IsAssignableFrom<T>()
Boolean IsAssignableTo<T>()
Boolean IsAssignableTo(Type)
referenceBoolean IsGenericMethodParameter()
reference
Boolean TryFormat(Span<Char>, Int32&, ReadOnlySpan<Char>, IFormatProvider)
reference
Boolean TryFormat(Span<Char>, Int32&, ReadOnlySpan<Char>, IFormatProvider)
reference
Boolean TryFormat(Span<Char>, Int32&, ReadOnlySpan<Char>, IFormatProvider)
reference
String[] GetNames<TEnum>()
referenceTEnum[] GetValues<TEnum>()
referenceTEnum Parse<TEnum>(String)
referenceTEnum Parse<TEnum>(String, Boolean)
referenceTEnum Parse<TEnum>(ReadOnlySpan<Char>)
referenceTEnum Parse<TEnum>(ReadOnlySpan<Char>, Boolean)
reference
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
String Join(Char, String[])
referenceString Join(Char, Object[])
referenceString Join(Char, String[], Int32, Int32)
referenceString Join<T>(Char, IEnumerable<T>)
reference
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
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
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
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
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
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
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
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
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
If any of the below reference are not included, the related polyfills will be disabled.
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'))" />
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'))" />
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'" />
Given the following class
class NullabilityTarget
{
public string? StringField;
public string?[] ArrayField;
public Dictionary<string, object?> GenericField;
}
[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);
}
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 theNullabilityInfo
for the target info.GetNullability
: returns theNullabilityState
for the state (NullabilityInfo.ReadState
orNullabilityInfo.WriteState
depending on which has more info) of target info.IsNullable
: given the state (NullabilityInfo.ReadState
orNullabilityInfo.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
.
- Returns true if state is
https://github.com/Tyrrrz/PolyShim
https://github.com/Sergio0694/PolySharp
https://github.com/theraot/Theraot
- https://github.com/manuelroemer/Nullable
- https://github.com/bgrainger/IndexRange
- https://github.com/manuelroemer/IsExternalInit
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.
Crack designed by Adrien Coquet from The Noun Project.