В соответствии с этим вопросом мне нужно указать, что 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)
Хорошо бы знать, какие ошибки выдает компилятор. Обычно они помогают понять, почему он отказывается что-то компилировать. Вероятно, это из-за того, что GroupCollection реализует как IEnumerable<Group>, так и IEnumerable<KeyValuePair<string,System.Text.RegularExpressions.Group>>, но ошибка компилятора действительно прояснила бы это.
@AlexeiLevenkov добавил скриншот ошибки. По крайней мере для меня это странно...
@moreON то же самое, пожалуйста, проверьте скриншот. Так вы говорите, что об этом нужно сообщить в Microsoft?
Проблема в том, что 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();
КРЫСЫ. Ты подтолкнул меня на это! Это, насколько мне известно, правильный ответ.
Должен ли он просто выбрать один, или это было бы небезопасным поведением?
@meJustAndrew, когда есть двусмысленность, разработчик должен удалить ее, явно указав, какой из них использовать.
Другой способ устранить неоднозначность методов Linq — указать тип для параметров выражения. например new Foo().Select( (string i) => "" );
Хороший отзыв, @moreON! Я включил это в свой ответ.
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 имеет поведение фильтрации, из-за чего мне неудобно использовать его для кастинга.
Неправда - и Regex.Match("aa", @"key : (.+?)-").Groups.ToArray<System.Text.RegularExpressions.Group>(), и Regex.Match("aa", @"key : (.+?)-").Groups.ToArray<KeyValuePair<string,System.Text.RegularExpressions.Group>>() прекрасно компилируются...