IEnumerable <char> в строку

Я никогда не сталкивался с этим раньше, но теперь я удивлен, что не могу найти простой способ конвертировать IEnumerable<char> в string.

Лучший способ, о котором я могу думать, - string str = new string(myEnumerable.ToArray());, но для меня кажется, что это создаст новый char[], а затем создаст новый string из этого, что кажется дорогим.

Я бы подумал, что это будет обычная функциональность, встроенная в .NET framework где-то. Есть ли более простой способ сделать это?

Для тех, кто заинтересован, причина, по которой я хотел бы использовать это, - использовать LINQ для фильтрации строк:

string allowedString = new string(inputString.Where(c => allowedChars.Contains(c)).ToArray());

Ответ 1

Вы можете использовать String.Concat().

var allowedString = String.Concat(
    inputString.Where(c => allowedChars.Contains(c))
);

Предостережение. Этот подход будет иметь некоторые последствия для производительности. String.Concat не содержит специальных наборов символов, поэтому он выполняет так, как если бы каждый символ был преобразован в строку, а затем конкатенирован, как упоминалось в документации ( и на самом деле он). Конечно, это дает вам встроенный способ выполнить эту задачу, но это может быть сделано лучше.

Я не думаю, что в рамках каких-либо реализаций есть специальный случай char, поэтому вам придется его реализовать. Простой набор символов, добавляемых в строковый построитель, достаточно прост для создания.


Вот некоторые тесты, которые я взял на dev-машине, и это выглядит правильно.

1000000 итераций в последовательности из 300 символов в 32-разрядной версии:

ToArrayString:        00:00:03.1695463
Concat:               00:00:07.2518054
StringBuilderChars:   00:00:03.1335455
StringBuilderStrings: 00:00:06.4618266
static readonly IEnumerable<char> seq = Enumerable.Repeat('a', 300);

static string ToArrayString(IEnumerable<char> charSequence)
{
    return new String(charSequence.ToArray());
}

static string Concat(IEnumerable<char> charSequence)
{
    return String.Concat(charSequence);
}

static string StringBuilderChars(IEnumerable<char> charSequence)
{
    var sb = new StringBuilder();
    foreach (var c in charSequence)
    {
        sb.Append(c);
    }
    return sb.ToString();
}

static string StringBuilderStrings(IEnumerable<char> charSequence)
{
    var sb = new StringBuilder();
    foreach (var c in charSequence)
    {
        sb.Append(c.ToString());
    }
    return sb.ToString();
}

Ответ 2

Я сделал это вопросом другого вопроса, но все больше и больше, что становится прямым ответом на этот вопрос.

Я провел несколько тестов производительности трех простых методов преобразования IEnumerable<char> в string, эти методы

новая строка

return new string(charSequence.ToArray());

Concat

return string.Concat(charSequence)

StringBuilder

var sb = new StringBuilder();
foreach (var c in charSequence)
{
    sb.Append(c);
}

return sb.ToString();

В моем тестировании, подробно описанном в связанном вопросе, для 1000000 итераций "Some reasonably small test data" я получаю такие результаты,

1000000 итераций "Конкат" заняло 1597 мс.

1000000 итераций "новой строки" заняло 869 мс.

1000000 итераций "StringBuilder" заняло 748 мс.

Это говорит мне, что нет веских оснований для использования string.Concat для этой задачи. Если вы хотите простоту использовать подход новая строка, и если хотите, чтобы производительность использовала StringBuilder.

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

Ответ 3

Начиная с .NET 4, многие строковые методы принимают IEnumerable в качестве аргументов.

string.Concat(myEnumerable);

Ответ 4

Мои данные противоречат результатам, опубликованным Джодрелом. Сначала рассмотрим методы расширения, которые я использую:

public static string AsStringConcat(this IEnumerable<char> characters)
{        
    return String.Concat(characters);
}

public static string AsStringNew(this IEnumerable<char> characters)
{
    return new String(characters.ToArray());
}

public static string AsStringSb(this IEnumerable<char> characters)
{
    StringBuilder sb = new StringBuilder();
    foreach (char c in characters)
    {
        sb.Append(c);
    }
    return sb.ToString();
}

Мои результаты

С

  • STRLEN = 31
  • Итерации = 1000000

Ввод

  • ((IEnumerable<char>)RandomString(STRLEN)).Reverse()

Результаты

  • Concat: 1x
  • Новое: 3x
  • StringBuilder: 3x

Ввод

  • ((IEnumerable<char>)RandomString(STRLEN)).Take((int)ITERATIONS/2)

Результаты

  • Concat: 1x
  • Новое: 7x
  • StringBuilder: 7x

Ввод

  • ((IEnumerable<char>)RandomString(STRLEN)) (это просто ускорение)

Результаты

  • Concat: 0 мс
  • Новое: 2000 мс
  • StringBuilder: 2000 мс
  • Вниз: 0 мс

Я запускал это на платформе Intel i5 760, ориентированной на .NET Framework 3.5.

Ответ 5

Вот более сжатая версия ответа StringBuilder:

return charSequence.Aggregate(new StringBuilder(), (seed, c) => seed.Append(c)).ToString();

Я приурочил это, используя те же тесты, что и Джефф Меркадо, и это было на 1 секунду медленнее на протяжении 1,000,000 итераций на той же последовательности из 300 символов (32-разрядная версия), чем более явная:

static string StringBuilderChars(IEnumerable<char> charSequence)
{
    var sb = new StringBuilder();
    foreach (var c in charSequence)
    {
        sb.Append(c);
    }
    return sb.ToString();
}

Итак, если вы поклонник аккумуляторов, то здесь вы идете.

Ответ 6

Другая возможность заключается в использовании

string.Join("", myEnumerable);

Я не измерял производительность.