Understanding OverloadResolutionPriority attribute in C# 13

 
 
  • Gérald Barré

C# 13 adds support for a new attribute: [OverloadResolutionPriority]. This attribute is niche, so most of you won't use it. However, it's a nice addition to the language, and it's worth knowing about it if you are writing libraries.

Let's consider this simple example:

C#
Sample.Test(); // Print: Test 1

static class Sample
{
    public static void Test() => Console.WriteLine("Test 1");
}

Now, you want to update the signature with a new optional parameter:

C#
Sample.Test(); // Print: Test 1

static class Sample
{
    // ❌ Binary breaking change as the method with no parameter doesn't exist anymore
    public static void Test([CallerMemberName]string name = "") => Console.WriteLine("Test");
}

A better solution would be to add a new overload, but it doesn't work:

C#
Sample.Test(); // Print: Test 1

static class Sample
{
    public static void Test() => Console.WriteLine("Test 1");

    // ❌ No more breaking change, but the new method is not called
    public static void Test([CallerMemberName]string name = "")
        => Console.WriteLine("Test 2");
}

The application will write "Test 1" to the console. The reason is that the compiler prefers the overload without parameters over the one with a default parameter as it matches the call site better.

Note that removing the first overload would be a binary breaking change. This is something that you cannot do in a library. The new [OverloadResolutionPriority] attribute allow to specify the priority of the overload resolution to the compiler. So, the compiler will choose the compatible overload with the highest priority.

C#
Sample.Test(); // Print: Test 2

static class Sample
{
    [OverloadResolutionPriority(-1)]
    public static void Test() => Console.WriteLine("Test 1");
    public static void Test([CallerMemberName]string name = "") => Console.WriteLine("Test 2");
}

Using the attribute, you can now add a new overload without breaking the existing code. The compiler will choose the overload with the highest priority!

You can also use this attribute to prioritize some overloads that would require implicit conversions:

C#
Sample.Test(1);    // Print: byte
Sample.Test(1000); // Print: int

static class Sample
{
    public static void Test(int a) => Console.WriteLine("int");

    [OverloadResolutionPriority(1)]
    public static void Test(byte  a) => Console.WriteLine("byte");
}

#Polyfill

The attribute is available in .NET 9. If you are targeting a previous version of .NET and you need to use this attribute, you can use a polyfill. You can use an existing NuGet package such as Meziantou.Polyfill or create your own implementation:

C#
namespace System.Runtime.CompilerServices;

/// <summary>
/// Specifies the priority of a member in overload resolution. When unspecified, the default priority is 0.
/// </summary>
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Property, AllowMultiple = false, Inherited = false)]
internal sealed class OverloadResolutionPriorityAttribute : Attribute
{
    /// <summary>
    /// Initializes a new instance of the <see cref="OverloadResolutionPriorityAttribute"/> class.
    /// </summary>
    /// <param name="priority">The priority of the attributed member. Higher numbers are prioritized, lower numbers are deprioritized. 0 is the default if no attribute is present.</param>
    public OverloadResolutionPriorityAttribute(int priority)
    {
        Priority = priority;
    }

    /// <summary>
    /// The priority of the member.
    /// </summary>
    public int Priority { get; }
}

#Further reading

Do you have a question or a suggestion about this post? Contact me!

Follow me:
Enjoy this blog?Buy Me A Coffee💖 Sponsor on GitHub