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
https://nuget.org/packages/Polyfill/
This project leverages features 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": "7.0.202",
"rollForward": "latestFeature"
}
}
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
{
private int member;
public int Member
{
get => member;
init => member = value;
}
}
Reference: Nullable reference types
[AllowNull]
[DisallowNull]
[DoesNotReturn]
[DoesNotReturnIf]
[MaybeNull]
[MaybeNullWhen]
[MemberNotNull]
[MemberNotNullWhen]
[NotNull]
[NotNullIfNotNull]
[NotNullWhen]
Reference: C# required modifier
public class Person
{
public Person() { }
[SetsRequiredMembers]
public Person(string name) =>
Name = name;
public required string Name { get; init; }
}
Reference: CompilerFeatureRequiredAttribute
Indicates that compiler support for a particular feature is required for the location where this attribute is applied.
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]);
}
}
}
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);
}
}
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);
}
}
https://github.com/Sergio0694/PolySharp
- 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.
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)
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'" />
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'" />
Crack designed by Adrien Coquet from The Noun Project.