Почему компилятор выбирает метод расширения для строки вместо неявного массива символов?

Если у меня есть импорт System.Linq, я могу использовать эту перегрузку ToArray в следующем вызове:

var x = "foo".ToArray();

а x назначается char[] с тремя элементами, которые являются символами из строки "foo". Затем, если я добавлю пользовательский метод расширения в область видимости:

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

Компилятор молча меняет свое мнение, и x становится string[] с одним элементом, который является строкой "foo".

Почему компилятор не жаловался на неоднозначность? Я знаю, что некоторые кажущиеся неоднозначными ситуации автоматически разрешаются компилятором без ошибок, но я не могу найти никакой документации или ссылок на такие ситуации. По сути, кажется, что обработка string как string, а не неявного массива char, кажется предпочтительным поведением...


person rory.ap    schedule 30.12.2020    source источник
comment
Пейджинг доктора Джона Скита :)   -  person rory.ap    schedule 30.12.2020
comment
"foo" имеет статический тип string. T как string является более конкретным соответствием статическому типу, чем T как IEnumerable<char> после первого преобразования. Но доктор Скит может объяснить это лучше.   -  person madreflection    schedule 30.12.2020
comment
ToCharArray — это то, что вам нужно, но madreflection подходит для метода расширения.   -  person jjxtra    schedule 30.12.2020
comment
@jjxtra верно, вопрос не в лучших практиках, а строго в поведении компилятора.   -  person rory.ap    schedule 30.12.2020
comment
@jjxtra также спасибо за редактирование, я удалил this, чтобы убедиться, что мой пример правильный.   -  person rory.ap    schedule 30.12.2020


Ответы (1)


Первый метод расширения, на который вы ссылаетесь:

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 Overloading: https://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

person jjxtra    schedule 30.12.2020
comment
Есть ли какая-то документация, на которую вы можете мне указать, которая поддерживает это поведение? Как я уже сказал, я ничего не могу найти. - person rory.ap; 31.12.2020
comment
Добавил ссылку внизу ответа - person jjxtra; 31.12.2020
comment
Извините, но ссылка на справку по Visual Basic не слишком полезна. В статье используются ключевые слова для конкретного языка, такие как AddressOf, которые, по крайней мере, раздражают того, кто знает оба языка, и совершенно сбивают с толку того, кто вообще не знает VB. Наверняка есть ссылка на C#? - person rory.ap; 31.12.2020
comment
Я также добавил документ C#, который короче. Образцы кода VB менее важны, точки разрешения являются важными битами. - person jjxtra; 31.12.2020
comment
Также добавлена ​​спецификация языка C#, прокрутите примерно на 60% вниз, чтобы найти разрешение метода. - person jjxtra; 31.12.2020