为什么编译器选择字符串上的扩展方法而不是隐式字符数组?

问题描述

如果我导入了 System.Linq,我可以在以下调用中使用 this ToArray overload

var x = "foo".ToArray();

x 被分配了一个 char[],其中三个元素是字符串 "foo" 中的字符。然后,如果我在范围内添加自定义扩展方法

public static T[] ToArray<T>(this T toConvert) => new[] { toConvert };

编译器地改变了主意,x 变成了一个 string[],其中一个元素是字符串 "foo"

为什么编译器没有抱怨歧义?我知道一些看似模棱两可的情况会由编译器自动解决而不会出错,但我找不到关于此类情况的任何文档或参考资料。基本上,似乎将 string 视为 string 而不是 char 的隐式数组似乎是首选行为...

解决方法

您引用的第一个扩展方法:

public static TSource[] ToArray<TSource> (this System.Collections.Generic.IEnumerable<TSource> source);

IEnumerable<TSource> 转换为数组(泛型接口)。

你做的第二种扩展方法:

public static T[] ToArray<T>(this T toConvert) => new[] { toConvert };

将任何 T 对象转换为单个对象数组。由于这是一个没有接口的泛型类型,因此它优于采用具有泛型类型的接口的扩展方法。本质上,与具有泛型类型的接口相比,应用扩展是潜在类型的更大覆盖面。编译器更喜欢匹配扩展方法的具体类型,而不是匹配的接口。

C# 语言规范,向下大约 60% 找到 Method Overloadinghttps://docs.microsoft.com/en-us/dotnet/csharp/language-reference/language-specification/introduction

C# 重载解析: https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-7.3/improved-overload-candidates

VB 版本,虽然主要适用于 C#: https://docs.microsoft.com/en-us/dotnet/visual-basic/reference/language-specification/overload-resolution