Skip to main content
ANcpLua.Roslyn.Utilities provides comprehensive polyfills that enable modern C# features when targeting netstandard2.0. All polyfills are #if-gated and compile only on older target frameworks.

Packages

Two packages include polyfills:
PackageUse case
ANcpLua.Roslyn.Utilities.SourcesAnalyzers/generators that need both utilities AND polyfills
ANcpLua.Roslyn.Utilities.PolyfillsProjects that need polyfills only (no Roslyn dependency)
# For generators/analyzers
dotnet add package ANcpLua.Roslyn.Utilities.Sources

# For polyfills only
dotnet add package ANcpLua.Roslyn.Utilities.Polyfills

Available Polyfills

Language Features

PolyfillEnablesAvailable from
IsExternalInitrecord types and init properties.NET 5+
RequiredMemberAttributerequired modifier.NET 7+
CompilerFeatureRequiredAttributeCompiler feature gating.NET 7+
SetsRequiredMembersAttributeConstructor sets all required members.NET 7+
CallerArgumentExpressionAttributeArgument name capture.NET 6+
ParamCollectionAttributeC# 13 params collections.NET 9+

Nullable Attributes

PolyfillEnablesAvailable from
AllowNull, DisallowNullNullable input annotations.NET Core 3.1+
MaybeNull, NotNullNullable output annotations.NET Core 3.1+
MaybeNullWhen, NotNullWhenConditional null annotations.NET Core 3.1+
NotNullIfNotNullLinked nullability.NET Core 3.1+
DoesNotReturn, DoesNotReturnIfUnreachable code flow.NET Core 3.1+
MemberNotNull, MemberNotNullWhenMember null state.NET 5+

Trim/AOT Attributes

PolyfillEnablesAvailable from
DynamicallyAccessedMembersAttributeTrim-safe reflection.NET 5+
DynamicallyAccessedMemberTypesMember type flags.NET 5+
RequiresUnreferencedCodeAttributeTrim warning.NET 5+
RequiresDynamicCodeAttributeAOT warning.NET 7+
UnconditionalSuppressMessageAttributeTrim suppression.NET 5+

Index and Range

PolyfillEnablesAvailable from
Indexarray[^1] syntax.NET Core 3.0+
Rangearray[1..3] syntax.NET Core 3.0+

Other

PolyfillEnablesAvailable from
StackTraceHiddenAttributeHide from stack traces.NET 6+
UnreachableExceptionUnreachable code paths.NET 7+
ExperimentalAttributeMark APIs as experimental.NET 8+
TimeProviderTestable time abstraction.NET 8+
LockSystem.Threading.Lock.NET 9+
String extensionsstring.Contains(StringComparison).NET Core 2.1+

Opt-Out Control

All polyfills are enabled by default. Use MSBuild properties to opt out:
<PropertyGroup>
  <!-- Disable individual categories -->
  <InjectIsExternalInitOnLegacy>false</InjectIsExternalInitOnLegacy>
  <InjectNullabilityAttributesOnLegacy>false</InjectNullabilityAttributesOnLegacy>
  <InjectTrimAttributesOnLegacy>false</InjectTrimAttributesOnLegacy>
  <InjectIndexRangeOnLegacy>false</InjectIndexRangeOnLegacy>
  <InjectStackTraceHiddenOnLegacy>false</InjectStackTraceHiddenOnLegacy>
  <InjectUnreachableExceptionOnLegacy>false</InjectUnreachableExceptionOnLegacy>
  <InjectExperimentalAttributeOnLegacy>false</InjectExperimentalAttributeOnLegacy>
  <InjectTimeProviderPolyfill>false</InjectTimeProviderPolyfill>
  <InjectLockPolyfill>false</InjectLockPolyfill>
  <InjectStringExtensionsPolyfill>false</InjectStringExtensionsPolyfill>
  <InjectRequiredMemberOnLegacy>false</InjectRequiredMemberOnLegacy>
  <InjectCallerAttributesOnLegacy>false</InjectCallerAttributesOnLegacy>
  <InjectCompilerFeatureRequiredOnLegacy>false</InjectCompilerFeatureRequiredOnLegacy>
  <InjectSetsRequiredMembersOnLegacy>false</InjectSetsRequiredMembersOnLegacy>
  <InjectParamCollectionOnLegacy>false</InjectParamCollectionOnLegacy>

  <!-- Or disable ALL polyfills at once -->
  <InjectAllPolyfillsOnLegacy>false</InjectAllPolyfillsOnLegacy>
</PropertyGroup>
Polyfills are only active for netstandard2.0 and netstandard2.1 targets. .NET 5+ projects don’t need them — the #if guards ensure they compile away.

Conflict Resolution

If another package provides the same polyfills (e.g., PolySharp), disable the overlapping categories to avoid duplicate type definitions:
<PropertyGroup>
  <InjectIsExternalInitOnLegacy>false</InjectIsExternalInitOnLegacy>
  <InjectNullabilityAttributesOnLegacy>false</InjectNullabilityAttributesOnLegacy>
  <InjectIndexRangeOnLegacy>false</InjectIndexRangeOnLegacy>
</PropertyGroup>