Я только что прочитал интересную статью под названием Получение слишком мило с C # yield return
Это заставило меня задуматься о том, как лучше всего определить, является ли IEnumerable фактической перечисляемой коллекцией или это конечный автомат, созданный с помощью ключевого слова yield.
Например, вы можете изменить DoubleXValue (из статьи) на что-то вроде:
private void DoubleXValue(IEnumerable<Point> points)
{
if(points is List<Point>)
foreach (var point in points)
point.X *= 2;
else throw YouCantDoThatException();
}
Вопрос 1) Есть ли лучший способ сделать это?
Вопрос 2) Это то, что я должен беспокоиться при создании API?
c#
api-design
ConditionRacer
источник
источник
ICollection<T>
это был бы лучший выбор, поскольку не все коллекцииList<T>
. Например, массивыPoint[]
реализуют,IList<T>
но это не такList<T>
.IEnumerable
является неизменным в том смысле, что вы не можете изменять коллекцию (добавлять / удалять) при ее перечислении, однако, если объекты в списке являются изменяемыми, то разумно изменять их при перечислении списка.ToList
? msdn.microsoft.com/en-us/library/bb342261.aspx Он не определяет, сгенерирован ли он урожайностью, но вместо этого делает его неактуальным.Ответы:
Ваш вопрос, насколько я понимаю, кажется, основан на неверной предпосылке. Позвольте мне посмотреть, смогу ли я восстановить аргументацию:
Проблема в том, что вторая предпосылка неверна. Даже если бы вы могли определить, был ли данный IEnumerable результатом преобразования блока итератора (и да, есть способы сделать это), это не помогло бы, потому что предположение неверно. Давайте проиллюстрируем почему.
Хорошо, у нас есть четыре метода. S1 и S2 - автоматически генерируемые последовательности; S3 и S4 - сгенерированные вручную последовательности. Теперь предположим, что у нас есть:
Результат для S1 и S4 будет 0; каждый раз, когда вы перечисляете последовательность, вы получаете новую ссылку на созданную букву M. Результат для S2 и S3 будет 100; каждый раз, когда вы перечисляете последовательность, вы получаете ту же ссылку на M, что и в прошлый раз. То, генерируется ли код последовательности автоматически или нет, является ортогональным к вопросу о том, имеют ли перечисленные объекты ссылочную идентичность или нет. Эти два свойства - автоматическая генерация и ссылочная идентичность - на самом деле не имеют ничего общего друг с другом. Статья, на которую вы ссылаетесь, несколько их объединяет.
Если поставщик последовательности не задокументирован как всегда предлагающий объекты, имеющие ссылочную идентичность , неразумно полагать, что это так.
источник
Я думаю, что ключевой концепцией является то, что вы должны рассматривать любую
IEnumerable<T>
коллекцию как неизменную : вы не должны изменять объекты из нее, вы должны создать новую коллекцию с новыми объектами:(Или напишите то же самое более кратко, используя LINQ
Select()
.)Если вы действительно хотите изменить элементы в коллекции, не используйте
IEnumerable<T>
, не используйтеIList<T>
(или, возможно,IReadOnlyList<T>
если вы не хотите изменять саму коллекцию, только элементы в ней, и вы находитесь на .Net 4.5):Таким образом, если кто-то попытается использовать ваш метод с
IEnumerable<T>
, он потерпит неудачу во время компиляции.источник
IEnumerable
более одного раза. Необходимость сделать это не раз может быть устранена путем вызоваToList()
и итерации по списку, хотя в конкретном случае, показанном OP, я предпочитаю решение svick о том, что DoubleXValue также возвращает IEnumerable. Конечно, в реальном коде я бы, вероятно, использовалLINQ
для генерации такого типаIEnumerable
. Тем не менее, выбор Свикаyield
имеет смысл в контексте вопроса ОП.Select()
. И в отношении нескольких итерацийIEnumerable
: ReSharper полезен в этом, потому что он может предупредить вас, когда вы это сделаете.Я лично считаю, что проблема, о которой говорилось в этой статье, связана с чрезмерным использованием
IEnumerable
интерфейса многими разработчиками C # в наши дни. Кажется, он стал типом по умолчанию, когда вам требуется набор объектов - но есть много информации, котораяIEnumerable
просто не говорит вам ... крайне важно: была ли она усовершенствована *.Если вы обнаружите, что вам нужно определить,
IEnumerable
действительно ли объект является aIEnumerable
или чем-то еще, абстракция просочилась, и вы, вероятно, находитесь в ситуации, когда ваш тип параметра немного слишком свободный. Если вам требуется дополнительная информация, котораяIEnumerable
сама по себе не предоставлена, сделайте это требование явным, выбрав один из других интерфейсов, таких какICollection
илиIList
.Редактировать для downvoters Пожалуйста, вернитесь и внимательно прочитайте вопрос. ОП запрашивает способ обеспечения того, чтобы
IEnumerable
экземпляры были материализованы перед передачей его методу API. Мой ответ на этот вопрос. Существует более широкая проблема, касающаяся правильного способа использованияIEnumerable
, и, конечно, ожидания кода, вставленного OP, основаны на неправильном понимании этой более широкой проблемы, но OP не спрашивал, как правильно использоватьIEnumerable
или как работать с неизменяемыми типами. , Нечестно опровергать меня за то, что я не отвечаю на вопрос, который не был поставлен.* Я использую термин
reify
, другие используютstabilize
илиmaterialize
. Я не думаю, что сообщество уже приняло общую конвенцию.источник
ICollection
иIList
заключается в том, что они изменчивы (или, по крайней мере, выглядят так).IReadOnlyCollection
иIReadOnlyList
из .Net 4.5 решить некоторые из этих проблем.