C# 13 中的 OverloadResolutionPriorityAttribute
Intro
C# 13 引入了 params collection 的 feature,可以参考我们之前的介绍 C# 13 新特性 params collection,不过有一个问题,我们之前也有提到就是如果我们要针对原来的数组新增 ReadOnlySpan
的重载可能会发生破坏性的变更,原来调用数组方法可能会变成调用 ReadOnlySpan
的方法重载,所以后面引入了 OverloadResolutionPriorityAttribute
来控制方法重载解析的优先级这样开发者可以为原有的方法指定一个较高的优先级来保证不会 break,之前我们也提到过这个 attribute 不过之前编译器还没支持,现在已经支持了
OverloadResolutionPriorityAttribute
Attribute 定义如下:
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)]
public 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; }
}
}
默认方法的 priority 是 0
,priority
越大优先级越高
Sample
来看个使用示例吧,首先我们回顾一下 params collection 里的用法
int[] numbers = [1, 2, 3, 4];
PrintNumbers(numbers);
ReadOnlySpan<int> numbersSpan = numbers.AsSpan();
PrintNumbers(numbersSpan);
PrintNumbers(1, 2, 3);
PrintNumbers([1, 2, 3]);
这里有两个 PrintNumbers
的方法,参数分别是数组和 ReadOnlySpan
,定义如下:
private static void PrintNumbers(params int[] numbers)
{
Console.WriteLine("PrintNumbers in Array overload");
foreach (var item in numbers)
{
Console.WriteLine(item);
}
}
private static void PrintNumbers(params ReadOnlySpan<int> numbers)
{
Console.WriteLine("PrintNumbers in ReadOnlySpan overload");
foreach (var item in numbers)
{
Console.WriteLine(item);
}
}
输出结果如下:
我们来说明一下输出结果,前面两个分别输出了 Array overload 和 ReadOnlySpan overload,因为我们传了具体的类型,所以编译器会优先找类型匹配的重载,所以分别找到了 Array 和 ReadOnlySpan 的方法重载
接着后面的两个调用使用 params collection 特性,因为没有指定特定的类型,所以编译器优先选择了 ReadOnlySpan 的重载来尽量使用性能更好的版本
接着我们来添加一个 OverloadResolutionPriorityAttribute
试一下, 我们来新加两个方法以便于和之前的代码对比
[OverloadResolutionPriority(1)]
private static void PrintNumbers1(params int[] numbers)
{
Console.WriteLine("PrintNumbers1 in Array overload");
foreach (var item in numbers)
{
Console.WriteLine(item);
}
}
private static void PrintNumbers1(params ReadOnlySpan<int> numbers)
{
Console.WriteLine("PrintNumbers1 in ReadOnlySpan overload");
foreach (var item in numbers)
{
Console.WriteLine(item);
}
}
这里我们针对数组的方法重载添加了一个 OverloadResolutionPriority(1)
的 attribute,我们再来测试一下
PrintNumbers1(1, 2, 3);
PrintNumbers1([1, 2, 3]);
输出结果如下:
可以看到在添加了 attribute 之后,原来调用 ReadOnlySpan
方法重载的变成了调用数组方法重载
More
OverloadResolutionPriorityAttribute
可以帮助我们告诉编译器选择符合预期的方法重载,应该只在发生重载冲突的时候再考虑使用,非必要不使用,使用了之后最好也要添加一些测试用例来验证是按照自己的预期工作的避免多个 overload 同时使用多个 priority 造成不符合预期的结果
References
https://github.com/dotnet/csharplang/issues/7706
https://github.com/dotnet/roslyn/issues/74067
https://github.com/dotnet/roslyn/issues/74131
https://github.com/dotnet/runtime/pull/102176
https://github.com/dotnet/runtime/issues/102173
https://github.com/dotnet/runtime/blob/v9.0.0-rc.1.24431.7/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/OverloadResolutionPriorityAttribute.cs
https://github.com/dotnet/csharplang/blob/main/proposals/csharp-13.0/overload-resolution-priority.md
https://github.com/WeihanLi/SamplesInPractice/blob/main/net9sample/CSharp13Samples/OverloadResolutionPrioritySample.cs