В соответствии с этим вопросом мне нужно указать, что 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>>()
прекрасно компилируются...