В чем причина string.Join нужно взять массив вместо IEnumerable?

Как говорится в заголовке: Почему string.Join нужно взять массив вместо IEnumerable? Это меня раздражает, так как я должен добавить .ToArray(), когда мне нужно создать объединенную строку из результата выражения LINQ.

Мой опыт говорит мне, что я пропустил что-то очевидное здесь.

Ответ 1

Перейдите на .NET 4.0 и используйте перегрузку, которая принимает IEnumerable<string>. В противном случае просто примите, что это была давняя проблема, которая не была решена до .NET 4.0. Вы можете устранить проблему, создав свой собственный метод расширения!

public static class StringEnumerableExtensions {
    public static string Join(this IEnumerable<string> strings, string separator) {
        return String.Join(separator, strings.ToArray());
    }
}

Использование:

IEnumerable<string> strings;
Console.WriteLine(strings.Join(", "));

Ответ 2

В .NET 4 были введены перегрузки Join, которые принимают аргумент IEnumerable<T> - если вы не используете .NET 4 то я боюсь, что вы застряли в передаче массива или создании собственной реализации.

Я предполагаю, что причина в том, что он не считался достаточно важным, когда структура была впервые разработана. IEnumerable<T> стал намного более заметным с введением LINQ.

(Конечно, не было никаких общих типов в .NET при его разработке, но нет причин, по которым они не могли бы сделать это с обычным не-общим IEnumerable, если бы они сочли это целесообразным. )

И нет причин, по которым вы не можете перевернуть свою собственную версию Join, которая принимает IEnumerable<T>, если вы считаете, что она вам нужна, и вы не можете перейти на .NET 4.

Ответ 3

Это не так..NET 4 добавил некоторые перегрузки, чтобы сделать это проще в использовании. В частности, вам не только не нужно проходить в массиве, но и не обязательно иметь последовательность строк. String.Join(String, IEnumerable<T>) будет вызывать ToString для каждого элемента последовательности.

Если вы не используете .NET 4, но выполняете много операций по объединению строк, вы всегда можете написать свои собственные методы, конечно.

Ответ 4

Я бы предположил, что String.Join требует возможности повторного итерации массива дважды (один раз для измерения длины и один раз для копирования). Некоторые классы, которые реализуют iEnumerable, могут быть успешно объединены в строковый массив, выполнив один проход для подсчета длины, вызвав Reset в перечислителе и используя второй проход для копирования данных, но так как iEnumerable не поддерживает ни свойство Capabilities, ни семейство производных классов, таких как iMultiPassEnumerable, единственные способы, с помощью которых String.Join может смело принять iEnumerable, будет либо (1) перечислять в некоторый тип списка, либо запустить соединение на нем, (2) угадать размер целевой строки и перераспределять по мере необходимости или (3) комбинировать подходы, группируя короткие строки в кластеры до, например, 8K, а затем объединение всех кластеров в конечный результат (который будет представлять собой смесь предварительно конкатенированных кластеров и длинных строк из исходного массива).

В то время как я, конечно же, буду признателен, что String.Join будет содержать накладные расходы, которые преобразуют iEnumerable в список, я не вижу, чтобы он обеспечивал большую эффективность, чем такое преобразование вручную (в отличие от массива версия String.Join, которая более эффективна, чем ручное объединение строк отдельно).