From 5c52ce70febe3640213a579272dbcdc77d4e34fe Mon Sep 17 00:00:00 2001 From: Paulo Morgado <470455+paulomorgado@users.noreply.github.com> Date: Mon, 13 Apr 2026 10:52:51 +0100 Subject: [PATCH 1/3] Add GC.AllocateUninitializedArray polyfill Add source-compatible polyfill for GC.AllocateUninitializedArray(int, bool) which was introduced in .NET 5.0. On older runtimes the implementation delegates to new T[length] (zero-initialized) for source compliance. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- api_list.include.md | 5 +++++ src/Consume/Consume.cs | 6 ++++++ src/Polyfill/GCPolyfill.cs | 23 +++++++++++++++++++++++ src/Split/net10.0/GCPolyfill.cs | 23 +++++++++++++++++++++++ src/Split/net11.0/GCPolyfill.cs | 23 +++++++++++++++++++++++ src/Split/net461/GCPolyfill.cs | 23 +++++++++++++++++++++++ src/Split/net462/GCPolyfill.cs | 23 +++++++++++++++++++++++ src/Split/net47/GCPolyfill.cs | 23 +++++++++++++++++++++++ src/Split/net471/GCPolyfill.cs | 23 +++++++++++++++++++++++ src/Split/net472/GCPolyfill.cs | 23 +++++++++++++++++++++++ src/Split/net48/GCPolyfill.cs | 23 +++++++++++++++++++++++ src/Split/net481/GCPolyfill.cs | 23 +++++++++++++++++++++++ src/Split/net5.0/GCPolyfill.cs | 23 +++++++++++++++++++++++ src/Split/net6.0/GCPolyfill.cs | 23 +++++++++++++++++++++++ src/Split/net7.0/GCPolyfill.cs | 23 +++++++++++++++++++++++ src/Split/net8.0/GCPolyfill.cs | 23 +++++++++++++++++++++++ src/Split/net9.0/GCPolyfill.cs | 23 +++++++++++++++++++++++ src/Split/netcoreapp2.0/GCPolyfill.cs | 23 +++++++++++++++++++++++ src/Split/netcoreapp2.1/GCPolyfill.cs | 23 +++++++++++++++++++++++ src/Split/netcoreapp2.2/GCPolyfill.cs | 23 +++++++++++++++++++++++ src/Split/netcoreapp3.0/GCPolyfill.cs | 23 +++++++++++++++++++++++ src/Split/netcoreapp3.1/GCPolyfill.cs | 23 +++++++++++++++++++++++ src/Split/netstandard2.0/GCPolyfill.cs | 23 +++++++++++++++++++++++ src/Split/netstandard2.1/GCPolyfill.cs | 23 +++++++++++++++++++++++ src/Split/uap10.0/GCPolyfill.cs | 23 +++++++++++++++++++++++ src/Tests/GCPolyfillTests.cs | 23 +++++++++++++++++++++++ 26 files changed, 563 insertions(+) create mode 100644 src/Polyfill/GCPolyfill.cs create mode 100644 src/Split/net10.0/GCPolyfill.cs create mode 100644 src/Split/net11.0/GCPolyfill.cs create mode 100644 src/Split/net461/GCPolyfill.cs create mode 100644 src/Split/net462/GCPolyfill.cs create mode 100644 src/Split/net47/GCPolyfill.cs create mode 100644 src/Split/net471/GCPolyfill.cs create mode 100644 src/Split/net472/GCPolyfill.cs create mode 100644 src/Split/net48/GCPolyfill.cs create mode 100644 src/Split/net481/GCPolyfill.cs create mode 100644 src/Split/net5.0/GCPolyfill.cs create mode 100644 src/Split/net6.0/GCPolyfill.cs create mode 100644 src/Split/net7.0/GCPolyfill.cs create mode 100644 src/Split/net8.0/GCPolyfill.cs create mode 100644 src/Split/net9.0/GCPolyfill.cs create mode 100644 src/Split/netcoreapp2.0/GCPolyfill.cs create mode 100644 src/Split/netcoreapp2.1/GCPolyfill.cs create mode 100644 src/Split/netcoreapp2.2/GCPolyfill.cs create mode 100644 src/Split/netcoreapp3.0/GCPolyfill.cs create mode 100644 src/Split/netcoreapp3.1/GCPolyfill.cs create mode 100644 src/Split/netstandard2.0/GCPolyfill.cs create mode 100644 src/Split/netstandard2.1/GCPolyfill.cs create mode 100644 src/Split/uap10.0/GCPolyfill.cs create mode 100644 src/Tests/GCPolyfillTests.cs diff --git a/api_list.include.md b/api_list.include.md index 2abfa41b..c63bdf21 100644 --- a/api_list.include.md +++ b/api_list.include.md @@ -370,6 +370,11 @@ * `void SetUnixFileMode(string, UnixFileMode)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.io.file.setunixfilemode?view=net-11.0#system-io-file-setunixfilemode(system-string-system-io-unixfilemode)) +#### GC + + * `T[] AllocateUninitializedArray(int, bool)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.gc.allocateuninitializedarray?view=net-11.0) + + #### Guid * `bool TryFormat(Span, int, ReadOnlySpan)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.guid.tryformat?view=net-11.0#system-guid-tryformat(system-span((system-byte))-system-int32@-system-readonlyspan((system-char)))) diff --git a/src/Consume/Consume.cs b/src/Consume/Consume.cs index d74cba8e..51d86f57 100644 --- a/src/Consume/Consume.cs +++ b/src/Consume/Consume.cs @@ -795,6 +795,12 @@ void Console_Methods() using var stderr = Console.OpenStandardErrorHandle(); } + void GC_Methods() + { + var array = GC.AllocateUninitializedArray(10); + var pinnedArray = GC.AllocateUninitializedArray(5, pinned: true); + } + void File_Methods() { const string TestFilePath = "testfile.txt"; diff --git a/src/Polyfill/GCPolyfill.cs b/src/Polyfill/GCPolyfill.cs new file mode 100644 index 00000000..7509272a --- /dev/null +++ b/src/Polyfill/GCPolyfill.cs @@ -0,0 +1,23 @@ +#if !NET5_0_OR_GREATER + +namespace Polyfills; + +using System; +using System.Runtime.CompilerServices; + +static partial class Polyfill +{ + extension(GC) + { + /// + /// Allocates an array of a specified type and length. Provides source-level compatibility + /// with newer APIs. On older runtimes the returned array is zero-initialized. + /// + //Link: https://learn.microsoft.com/en-us/dotnet/api/system.gc.allocateuninitializedarray?view=net-11.0 + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T[] AllocateUninitializedArray(int length, bool pinned = false) => + new T[length]; + + } +} +#endif diff --git a/src/Split/net10.0/GCPolyfill.cs b/src/Split/net10.0/GCPolyfill.cs new file mode 100644 index 00000000..7509272a --- /dev/null +++ b/src/Split/net10.0/GCPolyfill.cs @@ -0,0 +1,23 @@ +#if !NET5_0_OR_GREATER + +namespace Polyfills; + +using System; +using System.Runtime.CompilerServices; + +static partial class Polyfill +{ + extension(GC) + { + /// + /// Allocates an array of a specified type and length. Provides source-level compatibility + /// with newer APIs. On older runtimes the returned array is zero-initialized. + /// + //Link: https://learn.microsoft.com/en-us/dotnet/api/system.gc.allocateuninitializedarray?view=net-11.0 + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T[] AllocateUninitializedArray(int length, bool pinned = false) => + new T[length]; + + } +} +#endif diff --git a/src/Split/net11.0/GCPolyfill.cs b/src/Split/net11.0/GCPolyfill.cs new file mode 100644 index 00000000..7509272a --- /dev/null +++ b/src/Split/net11.0/GCPolyfill.cs @@ -0,0 +1,23 @@ +#if !NET5_0_OR_GREATER + +namespace Polyfills; + +using System; +using System.Runtime.CompilerServices; + +static partial class Polyfill +{ + extension(GC) + { + /// + /// Allocates an array of a specified type and length. Provides source-level compatibility + /// with newer APIs. On older runtimes the returned array is zero-initialized. + /// + //Link: https://learn.microsoft.com/en-us/dotnet/api/system.gc.allocateuninitializedarray?view=net-11.0 + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T[] AllocateUninitializedArray(int length, bool pinned = false) => + new T[length]; + + } +} +#endif diff --git a/src/Split/net461/GCPolyfill.cs b/src/Split/net461/GCPolyfill.cs new file mode 100644 index 00000000..7509272a --- /dev/null +++ b/src/Split/net461/GCPolyfill.cs @@ -0,0 +1,23 @@ +#if !NET5_0_OR_GREATER + +namespace Polyfills; + +using System; +using System.Runtime.CompilerServices; + +static partial class Polyfill +{ + extension(GC) + { + /// + /// Allocates an array of a specified type and length. Provides source-level compatibility + /// with newer APIs. On older runtimes the returned array is zero-initialized. + /// + //Link: https://learn.microsoft.com/en-us/dotnet/api/system.gc.allocateuninitializedarray?view=net-11.0 + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T[] AllocateUninitializedArray(int length, bool pinned = false) => + new T[length]; + + } +} +#endif diff --git a/src/Split/net462/GCPolyfill.cs b/src/Split/net462/GCPolyfill.cs new file mode 100644 index 00000000..7509272a --- /dev/null +++ b/src/Split/net462/GCPolyfill.cs @@ -0,0 +1,23 @@ +#if !NET5_0_OR_GREATER + +namespace Polyfills; + +using System; +using System.Runtime.CompilerServices; + +static partial class Polyfill +{ + extension(GC) + { + /// + /// Allocates an array of a specified type and length. Provides source-level compatibility + /// with newer APIs. On older runtimes the returned array is zero-initialized. + /// + //Link: https://learn.microsoft.com/en-us/dotnet/api/system.gc.allocateuninitializedarray?view=net-11.0 + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T[] AllocateUninitializedArray(int length, bool pinned = false) => + new T[length]; + + } +} +#endif diff --git a/src/Split/net47/GCPolyfill.cs b/src/Split/net47/GCPolyfill.cs new file mode 100644 index 00000000..7509272a --- /dev/null +++ b/src/Split/net47/GCPolyfill.cs @@ -0,0 +1,23 @@ +#if !NET5_0_OR_GREATER + +namespace Polyfills; + +using System; +using System.Runtime.CompilerServices; + +static partial class Polyfill +{ + extension(GC) + { + /// + /// Allocates an array of a specified type and length. Provides source-level compatibility + /// with newer APIs. On older runtimes the returned array is zero-initialized. + /// + //Link: https://learn.microsoft.com/en-us/dotnet/api/system.gc.allocateuninitializedarray?view=net-11.0 + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T[] AllocateUninitializedArray(int length, bool pinned = false) => + new T[length]; + + } +} +#endif diff --git a/src/Split/net471/GCPolyfill.cs b/src/Split/net471/GCPolyfill.cs new file mode 100644 index 00000000..7509272a --- /dev/null +++ b/src/Split/net471/GCPolyfill.cs @@ -0,0 +1,23 @@ +#if !NET5_0_OR_GREATER + +namespace Polyfills; + +using System; +using System.Runtime.CompilerServices; + +static partial class Polyfill +{ + extension(GC) + { + /// + /// Allocates an array of a specified type and length. Provides source-level compatibility + /// with newer APIs. On older runtimes the returned array is zero-initialized. + /// + //Link: https://learn.microsoft.com/en-us/dotnet/api/system.gc.allocateuninitializedarray?view=net-11.0 + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T[] AllocateUninitializedArray(int length, bool pinned = false) => + new T[length]; + + } +} +#endif diff --git a/src/Split/net472/GCPolyfill.cs b/src/Split/net472/GCPolyfill.cs new file mode 100644 index 00000000..7509272a --- /dev/null +++ b/src/Split/net472/GCPolyfill.cs @@ -0,0 +1,23 @@ +#if !NET5_0_OR_GREATER + +namespace Polyfills; + +using System; +using System.Runtime.CompilerServices; + +static partial class Polyfill +{ + extension(GC) + { + /// + /// Allocates an array of a specified type and length. Provides source-level compatibility + /// with newer APIs. On older runtimes the returned array is zero-initialized. + /// + //Link: https://learn.microsoft.com/en-us/dotnet/api/system.gc.allocateuninitializedarray?view=net-11.0 + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T[] AllocateUninitializedArray(int length, bool pinned = false) => + new T[length]; + + } +} +#endif diff --git a/src/Split/net48/GCPolyfill.cs b/src/Split/net48/GCPolyfill.cs new file mode 100644 index 00000000..7509272a --- /dev/null +++ b/src/Split/net48/GCPolyfill.cs @@ -0,0 +1,23 @@ +#if !NET5_0_OR_GREATER + +namespace Polyfills; + +using System; +using System.Runtime.CompilerServices; + +static partial class Polyfill +{ + extension(GC) + { + /// + /// Allocates an array of a specified type and length. Provides source-level compatibility + /// with newer APIs. On older runtimes the returned array is zero-initialized. + /// + //Link: https://learn.microsoft.com/en-us/dotnet/api/system.gc.allocateuninitializedarray?view=net-11.0 + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T[] AllocateUninitializedArray(int length, bool pinned = false) => + new T[length]; + + } +} +#endif diff --git a/src/Split/net481/GCPolyfill.cs b/src/Split/net481/GCPolyfill.cs new file mode 100644 index 00000000..7509272a --- /dev/null +++ b/src/Split/net481/GCPolyfill.cs @@ -0,0 +1,23 @@ +#if !NET5_0_OR_GREATER + +namespace Polyfills; + +using System; +using System.Runtime.CompilerServices; + +static partial class Polyfill +{ + extension(GC) + { + /// + /// Allocates an array of a specified type and length. Provides source-level compatibility + /// with newer APIs. On older runtimes the returned array is zero-initialized. + /// + //Link: https://learn.microsoft.com/en-us/dotnet/api/system.gc.allocateuninitializedarray?view=net-11.0 + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T[] AllocateUninitializedArray(int length, bool pinned = false) => + new T[length]; + + } +} +#endif diff --git a/src/Split/net5.0/GCPolyfill.cs b/src/Split/net5.0/GCPolyfill.cs new file mode 100644 index 00000000..7509272a --- /dev/null +++ b/src/Split/net5.0/GCPolyfill.cs @@ -0,0 +1,23 @@ +#if !NET5_0_OR_GREATER + +namespace Polyfills; + +using System; +using System.Runtime.CompilerServices; + +static partial class Polyfill +{ + extension(GC) + { + /// + /// Allocates an array of a specified type and length. Provides source-level compatibility + /// with newer APIs. On older runtimes the returned array is zero-initialized. + /// + //Link: https://learn.microsoft.com/en-us/dotnet/api/system.gc.allocateuninitializedarray?view=net-11.0 + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T[] AllocateUninitializedArray(int length, bool pinned = false) => + new T[length]; + + } +} +#endif diff --git a/src/Split/net6.0/GCPolyfill.cs b/src/Split/net6.0/GCPolyfill.cs new file mode 100644 index 00000000..7509272a --- /dev/null +++ b/src/Split/net6.0/GCPolyfill.cs @@ -0,0 +1,23 @@ +#if !NET5_0_OR_GREATER + +namespace Polyfills; + +using System; +using System.Runtime.CompilerServices; + +static partial class Polyfill +{ + extension(GC) + { + /// + /// Allocates an array of a specified type and length. Provides source-level compatibility + /// with newer APIs. On older runtimes the returned array is zero-initialized. + /// + //Link: https://learn.microsoft.com/en-us/dotnet/api/system.gc.allocateuninitializedarray?view=net-11.0 + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T[] AllocateUninitializedArray(int length, bool pinned = false) => + new T[length]; + + } +} +#endif diff --git a/src/Split/net7.0/GCPolyfill.cs b/src/Split/net7.0/GCPolyfill.cs new file mode 100644 index 00000000..7509272a --- /dev/null +++ b/src/Split/net7.0/GCPolyfill.cs @@ -0,0 +1,23 @@ +#if !NET5_0_OR_GREATER + +namespace Polyfills; + +using System; +using System.Runtime.CompilerServices; + +static partial class Polyfill +{ + extension(GC) + { + /// + /// Allocates an array of a specified type and length. Provides source-level compatibility + /// with newer APIs. On older runtimes the returned array is zero-initialized. + /// + //Link: https://learn.microsoft.com/en-us/dotnet/api/system.gc.allocateuninitializedarray?view=net-11.0 + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T[] AllocateUninitializedArray(int length, bool pinned = false) => + new T[length]; + + } +} +#endif diff --git a/src/Split/net8.0/GCPolyfill.cs b/src/Split/net8.0/GCPolyfill.cs new file mode 100644 index 00000000..7509272a --- /dev/null +++ b/src/Split/net8.0/GCPolyfill.cs @@ -0,0 +1,23 @@ +#if !NET5_0_OR_GREATER + +namespace Polyfills; + +using System; +using System.Runtime.CompilerServices; + +static partial class Polyfill +{ + extension(GC) + { + /// + /// Allocates an array of a specified type and length. Provides source-level compatibility + /// with newer APIs. On older runtimes the returned array is zero-initialized. + /// + //Link: https://learn.microsoft.com/en-us/dotnet/api/system.gc.allocateuninitializedarray?view=net-11.0 + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T[] AllocateUninitializedArray(int length, bool pinned = false) => + new T[length]; + + } +} +#endif diff --git a/src/Split/net9.0/GCPolyfill.cs b/src/Split/net9.0/GCPolyfill.cs new file mode 100644 index 00000000..7509272a --- /dev/null +++ b/src/Split/net9.0/GCPolyfill.cs @@ -0,0 +1,23 @@ +#if !NET5_0_OR_GREATER + +namespace Polyfills; + +using System; +using System.Runtime.CompilerServices; + +static partial class Polyfill +{ + extension(GC) + { + /// + /// Allocates an array of a specified type and length. Provides source-level compatibility + /// with newer APIs. On older runtimes the returned array is zero-initialized. + /// + //Link: https://learn.microsoft.com/en-us/dotnet/api/system.gc.allocateuninitializedarray?view=net-11.0 + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T[] AllocateUninitializedArray(int length, bool pinned = false) => + new T[length]; + + } +} +#endif diff --git a/src/Split/netcoreapp2.0/GCPolyfill.cs b/src/Split/netcoreapp2.0/GCPolyfill.cs new file mode 100644 index 00000000..7509272a --- /dev/null +++ b/src/Split/netcoreapp2.0/GCPolyfill.cs @@ -0,0 +1,23 @@ +#if !NET5_0_OR_GREATER + +namespace Polyfills; + +using System; +using System.Runtime.CompilerServices; + +static partial class Polyfill +{ + extension(GC) + { + /// + /// Allocates an array of a specified type and length. Provides source-level compatibility + /// with newer APIs. On older runtimes the returned array is zero-initialized. + /// + //Link: https://learn.microsoft.com/en-us/dotnet/api/system.gc.allocateuninitializedarray?view=net-11.0 + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T[] AllocateUninitializedArray(int length, bool pinned = false) => + new T[length]; + + } +} +#endif diff --git a/src/Split/netcoreapp2.1/GCPolyfill.cs b/src/Split/netcoreapp2.1/GCPolyfill.cs new file mode 100644 index 00000000..7509272a --- /dev/null +++ b/src/Split/netcoreapp2.1/GCPolyfill.cs @@ -0,0 +1,23 @@ +#if !NET5_0_OR_GREATER + +namespace Polyfills; + +using System; +using System.Runtime.CompilerServices; + +static partial class Polyfill +{ + extension(GC) + { + /// + /// Allocates an array of a specified type and length. Provides source-level compatibility + /// with newer APIs. On older runtimes the returned array is zero-initialized. + /// + //Link: https://learn.microsoft.com/en-us/dotnet/api/system.gc.allocateuninitializedarray?view=net-11.0 + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T[] AllocateUninitializedArray(int length, bool pinned = false) => + new T[length]; + + } +} +#endif diff --git a/src/Split/netcoreapp2.2/GCPolyfill.cs b/src/Split/netcoreapp2.2/GCPolyfill.cs new file mode 100644 index 00000000..7509272a --- /dev/null +++ b/src/Split/netcoreapp2.2/GCPolyfill.cs @@ -0,0 +1,23 @@ +#if !NET5_0_OR_GREATER + +namespace Polyfills; + +using System; +using System.Runtime.CompilerServices; + +static partial class Polyfill +{ + extension(GC) + { + /// + /// Allocates an array of a specified type and length. Provides source-level compatibility + /// with newer APIs. On older runtimes the returned array is zero-initialized. + /// + //Link: https://learn.microsoft.com/en-us/dotnet/api/system.gc.allocateuninitializedarray?view=net-11.0 + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T[] AllocateUninitializedArray(int length, bool pinned = false) => + new T[length]; + + } +} +#endif diff --git a/src/Split/netcoreapp3.0/GCPolyfill.cs b/src/Split/netcoreapp3.0/GCPolyfill.cs new file mode 100644 index 00000000..7509272a --- /dev/null +++ b/src/Split/netcoreapp3.0/GCPolyfill.cs @@ -0,0 +1,23 @@ +#if !NET5_0_OR_GREATER + +namespace Polyfills; + +using System; +using System.Runtime.CompilerServices; + +static partial class Polyfill +{ + extension(GC) + { + /// + /// Allocates an array of a specified type and length. Provides source-level compatibility + /// with newer APIs. On older runtimes the returned array is zero-initialized. + /// + //Link: https://learn.microsoft.com/en-us/dotnet/api/system.gc.allocateuninitializedarray?view=net-11.0 + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T[] AllocateUninitializedArray(int length, bool pinned = false) => + new T[length]; + + } +} +#endif diff --git a/src/Split/netcoreapp3.1/GCPolyfill.cs b/src/Split/netcoreapp3.1/GCPolyfill.cs new file mode 100644 index 00000000..7509272a --- /dev/null +++ b/src/Split/netcoreapp3.1/GCPolyfill.cs @@ -0,0 +1,23 @@ +#if !NET5_0_OR_GREATER + +namespace Polyfills; + +using System; +using System.Runtime.CompilerServices; + +static partial class Polyfill +{ + extension(GC) + { + /// + /// Allocates an array of a specified type and length. Provides source-level compatibility + /// with newer APIs. On older runtimes the returned array is zero-initialized. + /// + //Link: https://learn.microsoft.com/en-us/dotnet/api/system.gc.allocateuninitializedarray?view=net-11.0 + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T[] AllocateUninitializedArray(int length, bool pinned = false) => + new T[length]; + + } +} +#endif diff --git a/src/Split/netstandard2.0/GCPolyfill.cs b/src/Split/netstandard2.0/GCPolyfill.cs new file mode 100644 index 00000000..7509272a --- /dev/null +++ b/src/Split/netstandard2.0/GCPolyfill.cs @@ -0,0 +1,23 @@ +#if !NET5_0_OR_GREATER + +namespace Polyfills; + +using System; +using System.Runtime.CompilerServices; + +static partial class Polyfill +{ + extension(GC) + { + /// + /// Allocates an array of a specified type and length. Provides source-level compatibility + /// with newer APIs. On older runtimes the returned array is zero-initialized. + /// + //Link: https://learn.microsoft.com/en-us/dotnet/api/system.gc.allocateuninitializedarray?view=net-11.0 + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T[] AllocateUninitializedArray(int length, bool pinned = false) => + new T[length]; + + } +} +#endif diff --git a/src/Split/netstandard2.1/GCPolyfill.cs b/src/Split/netstandard2.1/GCPolyfill.cs new file mode 100644 index 00000000..7509272a --- /dev/null +++ b/src/Split/netstandard2.1/GCPolyfill.cs @@ -0,0 +1,23 @@ +#if !NET5_0_OR_GREATER + +namespace Polyfills; + +using System; +using System.Runtime.CompilerServices; + +static partial class Polyfill +{ + extension(GC) + { + /// + /// Allocates an array of a specified type and length. Provides source-level compatibility + /// with newer APIs. On older runtimes the returned array is zero-initialized. + /// + //Link: https://learn.microsoft.com/en-us/dotnet/api/system.gc.allocateuninitializedarray?view=net-11.0 + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T[] AllocateUninitializedArray(int length, bool pinned = false) => + new T[length]; + + } +} +#endif diff --git a/src/Split/uap10.0/GCPolyfill.cs b/src/Split/uap10.0/GCPolyfill.cs new file mode 100644 index 00000000..7509272a --- /dev/null +++ b/src/Split/uap10.0/GCPolyfill.cs @@ -0,0 +1,23 @@ +#if !NET5_0_OR_GREATER + +namespace Polyfills; + +using System; +using System.Runtime.CompilerServices; + +static partial class Polyfill +{ + extension(GC) + { + /// + /// Allocates an array of a specified type and length. Provides source-level compatibility + /// with newer APIs. On older runtimes the returned array is zero-initialized. + /// + //Link: https://learn.microsoft.com/en-us/dotnet/api/system.gc.allocateuninitializedarray?view=net-11.0 + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T[] AllocateUninitializedArray(int length, bool pinned = false) => + new T[length]; + + } +} +#endif diff --git a/src/Tests/GCPolyfillTests.cs b/src/Tests/GCPolyfillTests.cs new file mode 100644 index 00000000..745aa7aa --- /dev/null +++ b/src/Tests/GCPolyfillTests.cs @@ -0,0 +1,23 @@ +public class GCPolyfillTests +{ + [Test] + public async Task AllocateUninitializedArray() + { + var array = GC.AllocateUninitializedArray(10); + await Assert.That(array.Length).IsEqualTo(10); + } + + [Test] + public async Task AllocateUninitializedArrayPinned() + { + var array = GC.AllocateUninitializedArray(5, pinned: true); + await Assert.That(array.Length).IsEqualTo(5); + } + + [Test] + public async Task AllocateUninitializedArrayEmpty() + { + var array = GC.AllocateUninitializedArray(0); + await Assert.That(array.Length).IsEqualTo(0); + } +} From ddf9469c342468e499f8299479c27f7bb5cb60c8 Mon Sep 17 00:00:00 2001 From: Paulo Morgado <470455+paulomorgado@users.noreply.github.com> Date: Wed, 15 Apr 2026 09:59:58 +0100 Subject: [PATCH 2/3] Throw NotSupportedException when GC.AllocateUninitializedArray is called with pinned=true On older runtimes, pinned allocations cannot be performed, so the polyfill now throws NotSupportedException instead of silently ignoring the parameter. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/Consume/Consume.cs | 1 - src/Polyfill/GCPolyfill.cs | 12 +++++++++--- src/Tests/GCPolyfillTests.cs | 5 +++++ 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/Consume/Consume.cs b/src/Consume/Consume.cs index 51d86f57..db8921b0 100644 --- a/src/Consume/Consume.cs +++ b/src/Consume/Consume.cs @@ -798,7 +798,6 @@ void Console_Methods() void GC_Methods() { var array = GC.AllocateUninitializedArray(10); - var pinnedArray = GC.AllocateUninitializedArray(5, pinned: true); } void File_Methods() diff --git a/src/Polyfill/GCPolyfill.cs b/src/Polyfill/GCPolyfill.cs index 7509272a..7eb3203b 100644 --- a/src/Polyfill/GCPolyfill.cs +++ b/src/Polyfill/GCPolyfill.cs @@ -14,9 +14,15 @@ static partial class Polyfill /// with newer APIs. On older runtimes the returned array is zero-initialized. /// //Link: https://learn.microsoft.com/en-us/dotnet/api/system.gc.allocateuninitializedarray?view=net-11.0 - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static T[] AllocateUninitializedArray(int length, bool pinned = false) => - new T[length]; + public static T[] AllocateUninitializedArray(int length, bool pinned = false) + { + if (pinned) + { + throw new NotSupportedException("Pinned allocations are not supported on this runtime."); + } + + return new T[length]; + } } } diff --git a/src/Tests/GCPolyfillTests.cs b/src/Tests/GCPolyfillTests.cs index 745aa7aa..cb322e25 100644 --- a/src/Tests/GCPolyfillTests.cs +++ b/src/Tests/GCPolyfillTests.cs @@ -10,8 +10,13 @@ public async Task AllocateUninitializedArray() [Test] public async Task AllocateUninitializedArrayPinned() { +#if NET5_0_OR_GREATER var array = GC.AllocateUninitializedArray(5, pinned: true); await Assert.That(array.Length).IsEqualTo(5); +#else + await Assert.That(() => GC.AllocateUninitializedArray(5, pinned: true)) + .Throws(); +#endif } [Test] From 53a4d5b4270375288927c279e6e7631d2a0825ce Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Wed, 15 Apr 2026 09:02:32 +0000 Subject: [PATCH 3/3] Docs changes --- readme.md | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/readme.md b/readme.md index 225a7388..8ff2e3f8 100644 --- a/readme.md +++ b/readme.md @@ -113,7 +113,7 @@ This project uses features from the current stable SDK and C# language. As such | net5.0 | 9.5KB | 208.0KB | +198.5KB | +9.0KB | +6.5KB | +9.0KB | +14.0KB | | net6.0 | 10.0KB | 152.0KB | +142.0KB | +10.0KB | +7.0KB | +512bytes | +4.0KB | | net7.0 | 10.0KB | 117.5KB | +107.5KB | +9.0KB | +5.5KB | +512bytes | +4.0KB | -| net8.0 | 9.5KB | 89.0KB | +79.5KB | +8.5KB | +512bytes | +1.0KB | +3.5KB | +| net8.0 | 9.5KB | 89.0KB | +79.5KB | +9.0KB | +512bytes | +1.0KB | +3.5KB | | net9.0 | 9.5KB | 47.0KB | +37.5KB | +9.0KB | | +1.0KB | +3.5KB | | net10.0 | 10.0KB | 23.5KB | +13.5KB | +9.0KB | | +512bytes | +3.5KB | | net11.0 | 10.0KB | 18.5KB | +8.5KB | +9.0KB | | +512bytes | +3.5KB | @@ -124,23 +124,23 @@ This project uses features from the current stable SDK and C# language. As such | | Empty Assembly | With Polyfill | Diff | Ensure | ArgumentExceptions | StringInterpolation | Nullability | |----------------|----------------|---------------|-----------|-----------|--------------------|---------------------|-------------| | netstandard2.0 | 8.0KB | 436.4KB | +428.4KB | +16.7KB | +8.2KB | +13.9KB | +19.4KB | -| netstandard2.1 | 8.5KB | 363.0KB | +354.5KB | +16.7KB | +8.2KB | +14.4KB | +19.4KB | -| net461 | 8.5KB | 434.4KB | +425.9KB | +16.7KB | +8.2KB | +13.9KB | +19.4KB | -| net462 | 7.0KB | 437.9KB | +430.9KB | +16.7KB | +8.2KB | +14.4KB | +19.4KB | +| netstandard2.1 | 8.5KB | 362.9KB | +354.4KB | +16.7KB | +8.2KB | +14.4KB | +19.4KB | +| net461 | 8.5KB | 434.3KB | +425.8KB | +16.7KB | +8.2KB | +13.9KB | +19.4KB | +| net462 | 7.0KB | 437.8KB | +430.8KB | +16.7KB | +8.2KB | +14.4KB | +19.4KB | | net47 | 7.0KB | 437.6KB | +430.6KB | +16.7KB | +8.2KB | +13.9KB | +19.4KB | | net471 | 8.5KB | 437.6KB | +429.1KB | +16.7KB | +8.2KB | +13.9KB | +19.4KB | | net472 | 8.5KB | 435.0KB | +426.5KB | +16.7KB | +8.2KB | +13.9KB | +19.4KB | | net48 | 8.5KB | 435.0KB | +426.5KB | +16.7KB | +8.2KB | +13.9KB | +19.4KB | | net481 | 8.5KB | 435.0KB | +426.5KB | +16.7KB | +8.2KB | +13.9KB | +19.4KB | | netcoreapp2.0 | 9.0KB | 402.3KB | +393.3KB | +16.2KB | +8.2KB | +13.9KB | +18.9KB | -| netcoreapp2.1 | 9.0KB | 370.2KB | +361.2KB | +16.7KB | +8.7KB | +14.4KB | +19.4KB | -| netcoreapp2.2 | 9.0KB | 370.2KB | +361.2KB | +16.7KB | +8.7KB | +14.4KB | +19.4KB | -| netcoreapp3.0 | 9.5KB | 351.9KB | +342.4KB | +16.7KB | +8.2KB | +14.4KB | +19.4KB | +| netcoreapp2.1 | 9.0KB | 370.1KB | +361.1KB | +16.7KB | +8.7KB | +14.4KB | +19.4KB | +| netcoreapp2.2 | 9.0KB | 370.1KB | +361.1KB | +16.7KB | +8.7KB | +14.4KB | +19.4KB | +| netcoreapp3.0 | 9.5KB | 351.8KB | +342.3KB | +16.7KB | +8.2KB | +14.4KB | +19.4KB | | netcoreapp3.1 | 9.5KB | 350.3KB | +340.8KB | +16.7KB | +8.2KB | +13.9KB | +19.4KB | -| net5.0 | 9.5KB | 297.2KB | +287.8KB | +16.7KB | +8.2KB | +13.9KB | +19.4KB | -| net6.0 | 10.0KB | 223.5KB | +213.5KB | +17.7KB | +8.7KB | +1.1KB | +4.7KB | +| net5.0 | 9.5KB | 297.2KB | +287.7KB | +16.7KB | +8.2KB | +13.9KB | +19.4KB | +| net6.0 | 10.0KB | 223.4KB | +213.4KB | +17.7KB | +8.7KB | +1.1KB | +4.7KB | | net7.0 | 10.0KB | 170.2KB | +160.2KB | +16.6KB | +6.9KB | +1.1KB | +4.7KB | -| net8.0 | 9.5KB | 126.8KB | +117.3KB | +16.0KB | +811bytes | +1.6KB | +4.2KB | +| net8.0 | 9.5KB | 126.8KB | +117.3KB | +16.5KB | +811bytes | +1.6KB | +4.2KB | | net9.0 | 9.5KB | 67.8KB | +58.3KB | +16.5KB | | +1.6KB | +4.2KB | | net10.0 | 10.0KB | 35.8KB | +25.8KB | +16.5KB | | +1.1KB | +4.2KB | | net11.0 | 10.0KB | 27.4KB | +17.4KB | +16.5KB | | +1.1KB | +4.2KB | @@ -901,6 +901,11 @@ The class `Polyfill` includes the following extension methods: * `void SetUnixFileMode(string, UnixFileMode)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.io.file.setunixfilemode?view=net-11.0#system-io-file-setunixfilemode(system-string-system-io-unixfilemode)) +#### GC + + * `T[] AllocateUninitializedArray(int, bool)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.gc.allocateuninitializedarray?view=net-11.0) + + #### Guid * `bool TryFormat(Span, int, ReadOnlySpan)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.guid.tryformat?view=net-11.0#system-guid-tryformat(system-span((system-byte))-system-int32@-system-readonlyspan((system-char))))