Skip to main content

netstandard2.1 + Modern C# Polyfills

These three projects target netstandard2.1 so the engine can ship inside Unity (which historically pinned to .NET Standard 2.1 / Mono). They use LangVersion latest so modern C# syntax (records, init-only setters, pattern matching, file-scoped namespaces, etc.) works on top of the older BCL.

Hard rules

  • Never reference net8.0-only APIs. No System.Text.Json, no System.IO.Pipelines overloads added after netstandard2.1, no IUtf8SpanFormattable, no TimeProvider, no FrozenDictionary, no CollectionsMarshal.AsSpan on non-list types, no [StringSyntax] attribute, no anything from Microsoft.Extensions.* that requires net6+.
  • Polyfills bridge the gap. Look for existing polyfill files in each project (e.g., Polyfills/, CompilerServices/) before adding a new one. If a modern type is missing, add a minimal polyfill rather than changing the target framework.
  • Required attributes that need polyfills on netstandard2.1: IsExternalInit (init setters), RequiredMemberAttribute (required members), CompilerFeatureRequiredAttribute, SetsRequiredMembersAttribute, CallerArgumentExpressionAttribute, ModuleInitializerAttribute. These are all small shims under namespace System.Runtime.CompilerServices.
  • Span<T>/Memory<T> are fine — netstandard2.1 has them.
  • ValueTask is fine — netstandard2.1 has it.
  • IAsyncEnumerable<T> is fine — netstandard2.1 has it.
  • ref struct is fine at LangVersion latest, including ref fields (post-C# 11) — but verify the polyfill for UnscopedRefAttribute exists if you use [UnscopedRef].

Why this matters

If a Unity user pulls SECS.Engine.dll into a Unity project and it references a net8.0 type, the load will fail at runtime with a TypeLoadException that does not point at the offending API. Catching this at write-time is much cheaper than catching it at user-report-time.

When in doubt

Check each project's .csproj<TargetFramework>netstandard2.1</TargetFramework> is the signal. The example projects (examples/valenar/Host, examples/valenar/Server, examples/valenar/Generated, benchmarks/SECS.Benchmarks) target net8.0 and are unrestricted. The polyfill rule only applies to the three library projects above.