With .NET 9, we gain a new tool for managing method overloads: OverloadResolutionPriorityAttribute
declared in the namespace System.Runtime.CompilerServices
. It lets you set an overload’s priority, guiding the compiler in selecting a default for ambiguous calls.
In C#, method overloading allows multiple methods with the same name but different parameters. This can cause ambiguity as overloads grow. The compiler’s strict rules sometimes lead to unexpected outcomes or require verbose code to clarify the intended overload. This get even worse when ambiguity is caused by default parameters. For example the compiler emit an error in the following situation:
With the new attribute OverloadResolutionPriority
this situation can be fixed this way:
1 2 3 4 5 6 7 8 9 10 11 12 |
Class1.Method(); // Print Method A public static class Class1 { [System.Runtime.CompilerServices.OverloadResolutionPriority(3)] public static void Method(double d = 2) => Console.WriteLine("Method A"); [System.Runtime.CompilerServices.OverloadResolutionPriority(2)] public static void Method(int i = 1) => Console.WriteLine("Method B"); [System.Runtime.CompilerServices.OverloadResolutionPriority(1)] public static void Method(char c = ' ') => Console.WriteLine("Method C"); } |
OverloadResolutionPriority and implicit conversion
The C# compiler binding processing is a complex beast. Sometime it does some implicit conversion. For example here both calls prints Method B
.
1 2 3 4 5 6 7 |
Class1.Method(255); // Call Method B Class1.Method(256); // Call Method B public static class Class1 { public static void Method(byte b) => Console.WriteLine("Method A"); public static void Method(int i) => Console.WriteLine("Method B"); } |
The attribute OverloadResolutionPriority
can be used to avoid implicit conversion:
1 2 3 4 5 6 7 8 9 |
Class1.Method(255); // Call Method A Class1.Method(256); // Call Method B public static class Class1 { [System.Runtime.CompilerServices.OverloadResolutionPriority(1)] public static void Method(byte b) => Console.WriteLine("Method A"); public static void Method(int i) => Console.WriteLine("Method B"); } |
OverloadResolutionPriority for library developers
OverloadResolutionPriorityAttribute
is to provide library developers with a mechanism to influence the C# compiler’s method binding process. For instance, one might want to prioritizing the use of string.IndexOf(string, StringComparison = Ordinal)
over string.IndexOf(string)
.Let’s suppose you are publishing a library with this method Method()
:
1 2 3 |
public static class Class1 { public static void Method() => Console.WriteLine("Method A"); } |
You want to refactor Method()
to Method(int i = 123)
with a default parameter:
1 2 3 4 |
public static class Class1 { //public static void Method() => Console.WriteLine("Method A"); public static void Method(int i = 123) => Console.WriteLine("Method B"); } |
Doing so leads to a binary compatibility breaking change. If the client code doesn’t get recompiled a MissingMethodException
is thrown at runtime:
Thanks to OverloadResolutionPriorityAttribute
the library can be rewritten this way:
1 2 3 4 5 |
public static class Class1 { [System.Runtime.CompilerServices.OverloadResolutionPriority(-1)] public static void Method() => Console.WriteLine("Method A"); public static void Method(int i = 1) => Console.WriteLine("Method B"); } |
If the client code is recompiled it will print Method B
. If not recompiled it will print Method A
.
OverloadResolutionPriority usage in .NET 9
With NDepend, we analyzed .NET 9 Base Class Library assemblies within the directory C:\Program Files\dotnet\shared\Microsoft.NETCore.App\9.0.0
. Only the method Debug.Assert(bool)
is tagged so far with OverloadResolutionPriorityAttribute
:
This ensures that this overload is preferred over Debug.Assert(bool)
:
1 2 |
public static void Assert([DoesNotReturnIf(false)] bool condition, [CallerArgumentExpression("condition")] string? message = null) |
CallerArgumentExpression
attribute let’s capture as a string the expressions passed to a method. Doing so enables more informative error messages in diagnostic or testing APIs.Future Usage: Apply OverloadResolutionPriority to Span-based overloads
OverloadResolutionPriorityAttribute
to prioritize ReadOnlySpan<T>
extension methods over Span<T>
ones, especially when the latter simply delegate to the ReadOnlySpan<T>
-based implementation. This approach eliminates the extra call and reduces the pressure on the JIT to inline, while also preventing those additional methods from being retained during AOT trimming.