Skip to content

Commit d91a3ea

Browse files
committed
Update READMEs
1 parent 01e40d5 commit d91a3ea

5 files changed

Lines changed: 103 additions & 24 deletions

docs/NetEscapades.EnumGenerators.Generators.md

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ In general, we recommend installing the [NetEscapades.EnumGenerators](https://ww
3030
* [Avoiding runtime dependencies](#avoiding-runtime-dependencies)
3131
* [Choosing the correct packages for your scenario](#choosing-the-correct-packages-for-your-scenario)
3232
* [Enabling automatic interception (experimental)](#enabling-automatic-interception-experimental)
33+
* [Resolving overload ambiguity with `new()`](#resolving-overload-ambiguity-with-new)
3334
* [Preserving usages of the `[EnumExtensions]` attribute](#preserving-usages-of-the-enumextensions-attribute)<!-- endToc -->
3435

3536
## Why use these packages?
@@ -639,6 +640,47 @@ public void CantIntercept()
639640
```
640641
<!-- endInclude -->
641642

643+
<!-- include: overload-resolution. path: /fragments/overload-resolution.include.md -->
644+
## Resolving overload ambiguity with `new()`
645+
646+
The generated extension methods include overloads that accept option types such as `EnumParseOptions` and `SerializationOptions`. When using target-typed `new()` with these overloads, the C# compiler may report a CS0121 ambiguity error because it cannot determine which overload to call:
647+
648+
```csharp
649+
// CS0121: The call is ambiguous between 'Parse(string, StringComparison)' and 'Parse(string, EnumParseOptions)'
650+
var result = MyEnumExtensions.Parse("First", new());
651+
```
652+
653+
On **.NET 9+**, this is resolved automatically using the `[OverloadResolutionPriority]` attribute. The generator detects that the attribute type is available in the compilation and includes it in the generated code.
654+
655+
On **older target frameworks** (with C# 13+ language version), you can get the same behavior by providing a polyfill of the `OverloadResolutionPriorityAttribute` type. The generator will automatically detect the polyfill and include the attributeno additional configuration is needed.
656+
657+
The easiest way to add the polyfill is to use the [Polyfill](https://github.com/SimonCropp/Polyfill) NuGet package, which provides this and many other missing types for older frameworks.
658+
659+
Alternatively, you can add the attribute manually to your project:
660+
661+
```csharp
662+
namespace System.Runtime.CompilerServices
663+
{
664+
[AttributeUsage(
665+
AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Property,
666+
AllowMultiple = false, Inherited = false)]
667+
sealed class OverloadResolutionPriorityAttribute : Attribute
668+
{
669+
public OverloadResolutionPriorityAttribute(int priority) => Priority = priority;
670+
public int Priority { get; }
671+
}
672+
}
673+
```
674+
675+
If you need to disable the `[OverloadResolutionPriority]` attribute in the generated code, define the `NETESCAPADES_ENUMGENERATORS_OMIT_OVERLOAD_PRIORITY` preprocessor symbol:
676+
677+
```xml
678+
<PropertyGroup>
679+
<DefineConstants>$(DefineConstants);NETESCAPADES_ENUMGENERATORS_OMIT_OVERLOAD_PRIORITY</DefineConstants>
680+
</PropertyGroup>
681+
```
682+
<!-- endInclude -->
683+
642684
<!-- include: preserving-usages. path: /fragments/preserving-usages.include.md -->
643685
## Preserving usages of the `[EnumExtensions]` attribute
644686

docs/NetEscapades.EnumGenerators.Generators.source.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,4 +67,6 @@ This adds a `<PackageReference>` to your project. You can additionally mark the
6767

6868
include: interception-config
6969

70+
include: overload-resolution
71+
7072
include: preserving-usages

docs/NetEscapades.EnumGenerators.md

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ To change this file edit the source file and then run MarkdownSnippets.
3232
* [Avoiding runtime dependencies](#avoiding-runtime-dependencies)
3333
* [Choosing the correct packages for your scenario](#choosing-the-correct-packages-for-your-scenario)
3434
* [Enabling automatic interception (experimental)](#enabling-automatic-interception-experimental)
35+
* [Resolving overload ambiguity with `new()`](#resolving-overload-ambiguity-with-new)
3536
* [Preserving usages of the `[EnumExtensions]` attribute](#preserving-usages-of-the-enumextensions-attribute)<!-- endToc -->
3637

3738
## Why use these packages?
@@ -641,6 +642,47 @@ public void CantIntercept()
641642
```
642643
<!-- endInclude -->
643644

645+
<!-- include: overload-resolution. path: /fragments/overload-resolution.include.md -->
646+
## Resolving overload ambiguity with `new()`
647+
648+
The generated extension methods include overloads that accept option types such as `EnumParseOptions` and `SerializationOptions`. When using target-typed `new()` with these overloads, the C# compiler may report a CS0121 ambiguity error because it cannot determine which overload to call:
649+
650+
```csharp
651+
// CS0121: The call is ambiguous between 'Parse(string, StringComparison)' and 'Parse(string, EnumParseOptions)'
652+
var result = MyEnumExtensions.Parse("First", new());
653+
```
654+
655+
On **.NET 9+**, this is resolved automatically using the `[OverloadResolutionPriority]` attribute. The generator detects that the attribute type is available in the compilation and includes it in the generated code.
656+
657+
On **older target frameworks** (with C# 13+ language version), you can get the same behavior by providing a polyfill of the `OverloadResolutionPriorityAttribute` type. The generator will automatically detect the polyfill and include the attributeno additional configuration is needed.
658+
659+
The easiest way to add the polyfill is to use the [Polyfill](https://github.com/SimonCropp/Polyfill) NuGet package, which provides this and many other missing types for older frameworks.
660+
661+
Alternatively, you can add the attribute manually to your project:
662+
663+
```csharp
664+
namespace System.Runtime.CompilerServices
665+
{
666+
[AttributeUsage(
667+
AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Property,
668+
AllowMultiple = false, Inherited = false)]
669+
sealed class OverloadResolutionPriorityAttribute : Attribute
670+
{
671+
public OverloadResolutionPriorityAttribute(int priority) => Priority = priority;
672+
public int Priority { get; }
673+
}
674+
}
675+
```
676+
677+
If you need to disable the `[OverloadResolutionPriority]` attribute in the generated code, define the `NETESCAPADES_ENUMGENERATORS_OMIT_OVERLOAD_PRIORITY` preprocessor symbol:
678+
679+
```xml
680+
<PropertyGroup>
681+
<DefineConstants>$(DefineConstants);NETESCAPADES_ENUMGENERATORS_OMIT_OVERLOAD_PRIORITY</DefineConstants>
682+
</PropertyGroup>
683+
```
684+
<!-- endInclude -->
685+
644686
<!-- include: preserving-usages. path: /fragments/preserving-usages.include.md -->
645687
## Preserving usages of the `[EnumExtensions]` attribute
646688

docs/NetEscapades.EnumGenerators.source.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,4 +69,6 @@ This adds a `<PackageReference>` to your project. You can additionally mark the
6969

7070
include: interception-config
7171

72+
include: overload-resolution
73+
7274
include: preserving-usages

docs/README.md

Lines changed: 15 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<!--
22
GENERATED FILE - DO NOT EDIT
33
This file was generated by [MarkdownSnippets](https://github.com/SimonCropp/MarkdownSnippets).
4-
Source File: /docs/README.source.md
4+
Source File: /README.source.md
55
To change this file edit the source file and then run MarkdownSnippets.
66
-->
77

@@ -37,7 +37,7 @@ To change this file edit the source file and then run MarkdownSnippets.
3737

3838
## Why use these packages?
3939

40-
Many methods that work with enums are surprisingly slow. Calling `ToString()` or `HasFlag()` on an enum seems like it _should_ be fast, but it often isn't. This package provides a set of extension methods, such as `ToStringFast()` or `HasFlagFast()` that are designed to be very fast, with fewer allocations.<!-- include: benchmark. path: /docs/fragments/benchmark.include.md -->
40+
Many methods that work with enums are surprisingly slow. Calling `ToString()` or `HasFlag()` on an enum seems like it _should_ be fast, but it often isn't. This package provides a set of extension methods, such as `ToStringFast()` or `HasFlagFast()` that are designed to be very fast, with fewer allocations.<!-- include: benchmark. path: /fragments/benchmark.include.md -->
4141

4242

4343
For example, the following benchmark shows the advantage of calling `ToStringFast()` over `ToString()`:
@@ -100,7 +100,7 @@ This adds a `<PackageReference>` to your project:
100100
> [!WARNING]
101101
> You should _not_ use `PrivateAssets` when referencing the [NetEscapades.EnumGenerators](https://www.nuget.org/packages/NetEscapades.EnumGenerators) package, as the package has runtime dependencies. If you wish to avoid these runtime dependencies, [see below](#package-referencing-options) for alternative approaches.
102102
103-
Adding the package will automatically add a marker attribute, `[EnumExtensions]`, to your project.<!-- include: enum-usage. path: /docs/fragments/enum-usage.include.md -->
103+
Adding the package will automatically add a marker attribute, `[EnumExtensions]`, to your project.<!-- include: enum-usage. path: /fragments/enum-usage.include.md -->
104104

105105
To use the generator, add the `[EnumExtensions]` attribute to an enum. For example:
106106

@@ -348,7 +348,7 @@ or for external enums:
348348
```
349349
<!-- endInclude -->
350350

351-
<!-- include: usage-analyzers. path: /docs/fragments/usage-analyzers.include.md -->
351+
<!-- include: usage-analyzers. path: /fragments/usage-analyzers.include.md -->
352352
## Usage Analyzers
353353

354354
_NetEscapades.EnumGenerators_ includes optional analyzers that encourage the use of the generated extension methods instead of the built-in `System.Enum` methods. These analyzers can help improve performance by suggesting the faster, generated, alternatives like `ToStringFast()`, `HasFlagFast()`, and `TryParse()`.
@@ -419,7 +419,7 @@ All usage analyzers include automatic code fixes. When a diagnostic is triggered
419419

420420
![Demonstrating the code-fix option available in your IDE for each of the analyzers](https://raw.githubusercontent.com/andrewlock/NetEscapades.EnumGenerators/refs/heads/main/docs/images//code_fix.png)<!-- endInclude -->
421421

422-
<!-- include: package-referencing. path: /docs/fragments/package-referencing.include.md -->
422+
<!-- include: package-referencing. path: /fragments/package-referencing.include.md -->
423423
## Package referencing options
424424

425425
[NetEscapades.EnumGenerators](https://www.nuget.org/packages/NetEscapades.EnumGenerators) is a metapackage that references additional packages for functionality.
@@ -554,7 +554,7 @@ The final option is to reference [NetEscapades.EnumGenerators.Generators](https:
554554
555555
## Enabling automatic interception (experimental)
556556

557-
The main downside to the extension methods generated by [NetEscapades.EnumGenerators](https://www.nuget.org/packages/NetEscapades.EnumGenerators) is that you have to remember to use them. The [NetEscapades.EnumGenerators.Interceptors](https://www.nuget.org/packages/NetEscapades.EnumGenerators.Interceptors) package solves this problem by intercepting calls to `ToString()` and replacing them with calls `ToStringFast()` automatically using the .NET compiler feature called [interceptors](https://github.com/dotnet/roslyn/blob/main/docs/features/interceptors.md).<!-- include: interceptor-intro. path: /docs/fragments/interceptor-intro.include.md -->
557+
The main downside to the extension methods generated by [NetEscapades.EnumGenerators](https://www.nuget.org/packages/NetEscapades.EnumGenerators) is that you have to remember to use them. The [NetEscapades.EnumGenerators.Interceptors](https://www.nuget.org/packages/NetEscapades.EnumGenerators.Interceptors) package solves this problem by intercepting calls to `ToString()` and replacing them with calls `ToStringFast()` automatically using the .NET compiler feature called [interceptors](https://github.com/dotnet/roslyn/blob/main/docs/features/interceptors.md).<!-- include: interceptor-intro. path: /fragments/interceptor-intro.include.md -->
558558
559559

560560
For example, imagine you have this code, which uses the `Color` enum defined above:
@@ -585,7 +585,7 @@ dotnet add package NetEscapades.EnumGenerators.Interceptors
585585

586586
This adds a `<PackageReference>` to your project. You can additionally mark the package as `PrivateAssets="all"` and `ExcludeAssets="runtime"`, similarly to _NetEscapades.EnumGenerators_.
587587

588-
By default, adding [NetEscapades.EnumGenerators.Interceptors](https://www.nuget.org/packages/NetEscapades.EnumGenerators.Interceptors) to a project enables interception for all enums _defined in that project_ that use the `[EnumExtensions]` or `[EnumExtensions<T>]` attributes. If you wish to intercept calls made to enums with extensions defined in _other_ projects, you must add the `[Interceptable<T>]` attribute in the project where you want the interception to happen, e.g.<!-- include: interception-config. path: /docs/fragments/interception-config.include.md -->
588+
By default, adding [NetEscapades.EnumGenerators.Interceptors](https://www.nuget.org/packages/NetEscapades.EnumGenerators.Interceptors) to a project enables interception for all enums _defined in that project_ that use the `[EnumExtensions]` or `[EnumExtensions<T>]` attributes. If you wish to intercept calls made to enums with extensions defined in _other_ projects, you must add the `[Interceptable<T>]` attribute in the project where you want the interception to happen, e.g.<!-- include: interception-config. path: /fragments/interception-config.include.md -->
589589
590590
```csharp
591591
[assembly:Interceptable<DateTimeKind>]
@@ -642,7 +642,7 @@ public void CantIntercept()
642642
```
643643
<!-- endInclude -->
644644

645-
<!-- include: overload-resolution. path: /docs/fragments/overload-resolution.include.md -->
645+
<!-- include: overload-resolution. path: /fragments/overload-resolution.include.md -->
646646
## Resolving overload ambiguity with `new()`
647647

648648
The generated extension methods include overloads that accept option types such as `EnumParseOptions` and `SerializationOptions`. When using target-typed `new()` with these overloads, the C# compiler may report a CS0121 ambiguity error because it cannot determine which overload to call:
@@ -652,9 +652,9 @@ The generated extension methods include overloads that accept option types such
652652
var result = MyEnumExtensions.Parse("First", new());
653653
```
654654

655-
On **.NET 9+**, this is resolved automatically using the `[OverloadResolutionPriority]` attribute, which is included in the generated code gated behind a preprocessor directive.
655+
On **.NET 9+**, this is resolved automatically using the `[OverloadResolutionPriority]` attribute. The generator detects that the attribute type is available in the compilation and includes it in the generated code.
656656

657-
On **older target frameworks**, you can opt in to the same behavior by defining the `NETESCAPADES_ENUMGENERATORS_OVERLOAD_PRIORITY` preprocessor symbol and providing a polyfill of the `OverloadResolutionPriorityAttribute` type.
657+
On **older target frameworks** (with C# 13+ language version), you can get the same behavior by providing a polyfill of the `OverloadResolutionPriorityAttribute` type. The generator will automatically detect the polyfill and include the attributeno additional configuration is needed.
658658

659659
The easiest way to add the polyfill is to use the [Polyfill](https://github.com/SimonCropp/Polyfill) NuGet package, which provides this and many other missing types for older frameworks.
660660
@@ -674,25 +674,16 @@ namespace System.Runtime.CompilerServices
674674
}
675675
```
676676

677-
Then define the preprocessor symbol in your project file:
677+
If you need to disable the `[OverloadResolutionPriority]` attribute in the generated code, define the `NETESCAPADES_ENUMGENERATORS_OMIT_OVERLOAD_PRIORITY` preprocessor symbol:
678678

679679
```xml
680-
<Project Sdk="Microsoft.NET.Sdk">
681-
682-
<PropertyGroup>
683-
<OutputType>Exe</OutputType>
684-
<TargetFramework>net8.0</TargetFramework>
685-
<!-- Enable overload resolution priority for generated enum methods -->
686-
<DefineConstants>$(DefineConstants);NETESCAPADES_ENUMGENERATORS_OVERLOAD_PRIORITY</DefineConstants>
687-
</PropertyGroup>
688-
689-
<!-- Add the package -->
690-
<PackageReference Include="NetEscapades.EnumGenerators" Version="1.0.0-beta21" />
691-
</Project>
680+
<PropertyGroup>
681+
<DefineConstants>$(DefineConstants);NETESCAPADES_ENUMGENERATORS_OMIT_OVERLOAD_PRIORITY</DefineConstants>
682+
</PropertyGroup>
692683
```
693684
<!-- endInclude -->
694685

695-
<!-- include: preserving-usages. path: /docs/fragments/preserving-usages.include.md -->
686+
<!-- include: preserving-usages. path: /fragments/preserving-usages.include.md -->
696687
## Preserving usages of the `[EnumExtensions]` attribute
697688

698689
The `[EnumExtensions]` attribute is decorated with the `[Conditional]` attribute, [so their usage will not appear in the build output of your project](https://andrewlock.net/conditional-compilation-for-ignoring-method-calls-with-the-conditionalattribute/#applying-the-conditional-attribute-to-classes). If you use reflection at runtime on one of your `enum`s, you will not find `[EnumExtensions]` in the list of custom attributes. If you wish to preserve these attributes in the build output, you can define the `NETESCAPADES_ENUMGENERATORS_USAGES` MSBuild variable.

0 commit comments

Comments
 (0)