Почему невозможно вызвать .ToArray непосредственно в GroupCollection, даже если это IEnumerable?

В соответствии с этим вопросом мне нужно указать, что GroupCollection является IEnumerable, чтобы использовать методы расширения, которые работают с IEnumerable на нем:

GroupCollection groups =  Regex.Match(content, @"key : (.+?)-").Groups;

IEnumerable<Group> groupsEnumerable = groups;

var foo = groupsEnumerable.Select(x=>x.Value).ToArray(); //this works fine
var bar = groups.Select(x=>x.Value).ToArray(); //while this does not compile

Почему я должен сообщать экземпляру GroupCollection, что это экземпляр IEnumerable, чтобы вызывать на нем методы расширения, основанные на IEnumerable?

Добавлен скриншот ошибки (.NET 6)

Неправда - и Regex.Match("aa", @"key : (.+?)-").Groups.ToArray<System.Text.RegularExpressions.Group‌​>(), и Regex.Match("aa", @"key : (.+?)-").Groups.ToArray<KeyValuePair<string,System.Text.Regu‌​larExpressions.Group‌​>>() прекрасно компилируются...

Alexei Levenkov 22.02.2023 23:55

Хорошо бы знать, какие ошибки выдает компилятор. Обычно они помогают понять, почему он отказывается что-то компилировать. Вероятно, это из-за того, что GroupCollection реализует как IEnumerable<Group>, так и IEnumerable<KeyValuePair<string,System.Text.RegularExpressio‌​ns.Group>>, но ошибка компилятора действительно прояснила бы это.

moreON 23.02.2023 00:00

@AlexeiLevenkov добавил скриншот ошибки. По крайней мере для меня это странно...

meJustAndrew 23.02.2023 00:02

@moreON то же самое, пожалуйста, проверьте скриншот. Так вы говорите, что об этом нужно сообщить в Microsoft?

meJustAndrew 23.02.2023 00:03
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
4
57
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Проблема в том, что GroupCollection реализует две разные версии интерфейса IEnumerable<>:

public class GroupCollection : 
System.Collections.Generic.ICollection<System.Text.RegularExpressions.Group>,
System.Collections.Generic.IEnumerable<System.Collections.Generic.KeyValuePair<string,System.Text.RegularExpressions.Group>>,
System.Collections.Generic.IEnumerable<System.Text.RegularExpressions.Group>, 
System.Collections.Generic.IList<System.Text.RegularExpressions.Group>, 
System.Collections.Generic.IReadOnlyCollection<System.Collections.Generic.KeyValuePair<string,System.Text.RegularExpressions.Group>>, 
System.Collections.Generic.IReadOnlyCollection<System.Text.RegularExpressions.Group>, 
System.Collections.Generic.IReadOnlyDictionary<string,System.Text.RegularExpressions.Group>, 
System.Collections.Generic.IReadOnlyList<System.Text.RegularExpressions.Group>, 
System.Collections.IList

Компилятор не знает, какой универсальный тип передать в метод Enumerable.Select<>(): должен ли он использовать Group или KeyValuePair<string, Group>?

Вы можете увидеть такое же поведение из следующего кода:

new Foo().Select(i => i);

public class Foo : IEnumerable<int>, IEnumerable<string>
{
    IEnumerator<int> IEnumerable<int>.GetEnumerator() => throw new NotImplementedException();
    IEnumerator<string> IEnumerable<string>.GetEnumerator() => throw new NotImplementedException();
    IEnumerator IEnumerable.GetEnumerator() => throw new NotImplementedException();
}

Приводя его явно, вы устраняете эту двусмысленность. Чуть менее многословная (но менее эффективная) альтернатива — вызов .Cast<Group>()

var bar = groups.Cast<Group>().Select(x => x.Value).ToArray();

Или вы можете явно указать общие параметры для метода:

var bar = groups.Select<Group, string>(x => x.Value).ToArray();

Или укажите тип параметра в делегате, который вы передаете:

var bar = groups.Select((Group x) => x.Value).ToArray();

КРЫСЫ. Ты подтолкнул меня на это! Это, насколько мне известно, правильный ответ.

Josh Heaps 23.02.2023 00:06

Должен ли он просто выбрать один, или это было бы небезопасным поведением?

meJustAndrew 23.02.2023 00:06

@meJustAndrew, когда есть двусмысленность, разработчик должен удалить ее, явно указав, какой из них использовать.

Josh Heaps 23.02.2023 00:07

Другой способ устранить неоднозначность методов Linq — указать тип для параметров выражения. например new Foo().Select( (string i) => "" );

moreON 23.02.2023 00:11

Хороший отзыв, @moreON! Я включил это в свой ответ.

StriplingWarrior 23.02.2023 00:14

GroupCollection реализует два IEnumerable<T> интерфейса — IEnumerable<Group> и IEnumerable<KeyValuePair<string, Group>>, поэтому компилятор не может определить, какой из них использовать для Select (и других Enumerable методов расширения).

Другой подход — указать тип в labmda:

var bar = groups.Select((Group x) => x.Value).ToArray();

Или укажите общие параметры явно:

var bar = groups.Select<Group, string>(x => x.Value).ToArray();

Или используя OfType/Cast:

var bar = groups.OfType<Group>().Select(x => x.Value).ToArray();

Хотя последний теоретически может сломаться, если реализация внутреннего счетчика будет изменена.

OfType имеет поведение фильтрации, из-за чего мне неудобно использовать его для кастинга.

Theodor Zoulias 23.02.2023 07:33

Другие вопросы по теме