Skip to content

Commit

Permalink
Add OverloadResolutionPriority conceptual documentation. (#42394)
Browse files Browse the repository at this point in the history
* Publish feature speclet

Publish the C# 13 speclet for overload priority attribute.

* Update attributes language reference

* Final conceptual updates

* Warnings and wording.

* fix build warnings in our repo

* Add quick note on the priority values.
  • Loading branch information
BillWagner authored Sep 13, 2024
1 parent 7da7b4f commit f8c356e
Show file tree
Hide file tree
Showing 7 changed files with 57 additions and 2 deletions.
5 changes: 4 additions & 1 deletion docfx.json
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
"csharp-12.0/*.md",
"lock-object.md",
"method-group-natural-type-improvements.md",
"overload-resolution-priority.md",
"params-collections.md",
"ref-struct-interfaces.md",
"ref-unsafe-in-iterators-async.md"
Expand Down Expand Up @@ -492,7 +493,7 @@
"_csharplang/proposals/csharp-10.0/*.md": "08/07/2021",
"_csharplang/proposals/csharp-11.0/*.md": "09/30/2022",
"_csharplang/proposals/csharp-12.0/*.md": "08/15/2023",
"_csharplang/proposals/*.md": "08/14/2024",
"_csharplang/proposals/*.md": "08/30/2024",
"_roslyn/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 7.md": "11/08/2022",
"_roslyn/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 8.md": "09/26/2023",
"_roslyn/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 9.md": "06/26/2024",
Expand Down Expand Up @@ -666,6 +667,7 @@
"_csharplang/proposals/ref-unsafe-in-iterators-async.md": "Allow ref and unsafe in iterators and async methods",
"_csharplang/proposals/ref-struct-interfaces.md": "Allow ref struct types to implement some interfaces",
"_csharplang/proposals/partial-properties.md": "All partial properties and indexers",
"_csharplang/proposals/overload-resolution-priority.md": "Overload resolution priority tiebreaker attribute",
"_roslyn/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 7.md": "C# compiler breaking changes since C# 10",
"_roslyn/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 8.md": "C# compiler breaking changes since C# 11",
"_roslyn/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 9.md": "C# compiler breaking changes since C# 12",
Expand Down Expand Up @@ -785,6 +787,7 @@
"_csharplang/proposals/ref-unsafe-in-iterators-async.md": "This proposal modifies restrictions to enable ref local variables and unsafe blocks in iterators and async methods",
"_csharplang/proposals/ref-struct-interfaces.md": "This proposal provides features that enable interface authors to allow `ref struct` types to implement a particular interface",
"_csharplang/proposals/partial-properties.md": "This proposal provides for partial properties and indexers, allowing the definition of a property or indexer to be split across multiple parts.",
"_csharplang/proposals/overload-resolution-priority.md": "This proposal introduces a new attribute, `OverloadResolutionPriorityAttribute`, that can be applied to methods to influence overload resolution.",
"_roslyn/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 7.md": "Learn about any breaking changes since the initial release of C# 10",
"_roslyn/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 8.md": "Learn about any breaking changes since the initial release of C# 11",
"_roslyn/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 9.md": "Learn about any breaking changes since the initial release of C# 12",
Expand Down
15 changes: 15 additions & 0 deletions docs/csharp/language-reference/attributes/general.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ There are several attributes that can be applied to elements in your code that a
- [`ModuleInitializer`](#moduleinitializer-attribute): Declare a method that initializes a module.
- [`SkipLocalsInit`](#skiplocalsinit-attribute): Elide the code that initializes local variable storage to 0.
- [`UnscopedRef`](#unscopedref-attribute): Declare that a `ref` variable normally interpreted as `scoped` should be treated as unscoped.
- [`OverloadResolutionPriority`](#overloadresolutionpriority-attribute): Add a tiebreaker attribute to influence overload resolution for possibly ambiguous overloads.
- [`Experimental`](#experimental-attribute): Mark a type or member as experimental.

The compiler uses those semantic meanings to alter its output and report possible mistakes by developers using your code.
Expand Down Expand Up @@ -229,6 +230,20 @@ You add this attribute where the compiler treats a `ref` as implicitly `scoped`:

Applying the <xref:System.Diagnostics.CodeAnalysis.UnscopedRefAttribute?displayProperty=nameWithType> marks the element as unscoped.

## `OverloadResolutionPriority` attribute

The <xref:System.Runtime.CompilerServices.OverloadResolutionPriorityAttribute> enables library authors to prefer one overload over another when two overloads can be ambiguous. Its primary use case is for library authors to write better performing overloads while still supporting existing code without breaks.

For example, you might add a new overload that uses <xref:System.ReadOnlySpan%601> to reduce memory allocations:

:::code language="csharp" source="snippets/OrpaSnippets.cs" ID="SnippetOverloadExample":::

Overload resolution considers the two methods equally good for some argument types. For an argument of `int[]`, it prefers the first overload. To get the compiler to prefer the `ReadOnlySpan` version, you can increase the priority of that overload. The following example shows the effect of adding the attribute:

:::code language="csharp" source="snippets/OrpaSnippets.cs" ID="SnippetOrpaExample":::

All overloads with a lower priority than the highest overload priority are removed from the set of applicable methods. Methods without this attribute have the overload priority set to the default of zero. Library authors should use this attribute as a last resort when adding a new and better method overload. Library authors should have a deep understanding of how [Overload resolution](~/_csharplang/proposals/overload-resolution-priority.md#overload-resolution-priority) impacts choosing the better method. Otherwise, unexpected errors can result.

## See also

- <xref:System.Attribute>
Expand Down
25 changes: 25 additions & 0 deletions docs/csharp/language-reference/attributes/snippets/OrpaSnippets.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using System.Runtime.CompilerServices;

namespace attributes;
public class OverloadExample
{

public static void OrpaExample()
{
// <SnippetOrpaExample>
var d = new OverloadExample();
int[] arr = [1, 2, 3];
d.M(1, 2, 3, 4); // Prints "Span"
d.M(arr); // Prints "Span" when PriorityAttribute is applied
d.M([1, 2, 3, 4]); // Prints "Span"
d.M(1, 2, 3, 4); // Prints "Span"
// </SnippetOrpaExample>
}

// <SnippetOverloadExample>
[OverloadResolutionPriority(1)]
public void M(params ReadOnlySpan<int> s) => Console.WriteLine("Span");
// Default overload resolution priority of 0
public void M(params int[] a) => Console.WriteLine("Array");
// </SnippetOverloadExample>
}
3 changes: 3 additions & 0 deletions docs/csharp/language-reference/attributes/snippets/Program.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
using attributes;
using AttributeExamples;


OverloadExample.OrpaExample();

TraceExample.Main();
ObsoleteProgram.Main();
ModuleInitializerExampleMain.Main();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<TargetFramework>net9.0</TargetFramework>
<Nullable>enable</Nullable>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<ImplicitUsings>enable</ImplicitUsings>
Expand Down
2 changes: 2 additions & 0 deletions docs/csharp/specification/toc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,8 @@ items:
href: ../../../_csharplang/proposals/csharp-10.0/caller-argument-expression.md
- name: Extended nameof scope
href: ../../../_csharplang/proposals/csharp-11.0/extended-nameof-scope.md
- name: Overload resolution priority
href: ../../../_csharplang/proposals/overload-resolution-priority.md
- name: Statements
items:
- name: Global using directive
Expand Down
7 changes: 7 additions & 0 deletions docs/csharp/whats-new/csharp-13.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ C# 13 includes the following new features. You can try these features using the
- [Enable `ref struct` types to implement interfaces](#ref-struct-interfaces).
- [Allow ref struct types](#allows-ref-struct) as arguments for type parameters in generics.
- [Partial properties and indexers](#more-partial-members) are now allowed in `partial` types.
- [Overload resolution priority](#overload-resolution-priority) allows library authors to designate one overload as better than others.

C# 13 is supported on **.NET 9**. For more information, see [C# language versioning](../language-reference/configure-language-version.md).

Expand Down Expand Up @@ -101,6 +102,12 @@ This enables types such as <xref:System.Span%601?displayProperty=nameWithType> a

You can declare `partial` properties and `partial` indexers in C# 13. Partial properties and indexers generally follow the same rules as `partial` methods: you create one *declaring declaration* and one *implementing declaration*. The signatures of the two declarations must match. One restriction is that you can't use an auto-property declaration for a partial property. Properties that don't declare a body are considered the *declaring declaration*. You can learn more in the article on [partial members](../language-reference/keywords/partial-member.md).

## Overload resolution priority

In C# 13, the compiler recognizes the <xref:System.Runtime.CompilerServices.OverloadResolutionPriorityAttribute> to prefer one overload over another. Library authors can use this attribute to ensure that a new, better overload is preferred over an existing overload. For example, you might add a new overload that's more performant. You don't want to break existing code that uses your library, but you want users to update to the new version when they recompile. You can use [Overload resolution priority](../language-reference/attributes/general.md#overloadresolutionpriority-attribute) to inform the compiler which overload should be preferred. Overloads with the highest priority are preferred.

This feature is intended for library authors to avoid ambiguity when adding new overloads. Library authors should use care with this attribute to avoid confusion.

## See also

- [What's new in .NET 9](../../core/whats-new/dotnet-9/overview.md)

0 comments on commit f8c356e

Please sign in to comment.