Название - вопрос. Ниже приведена моя попытка ответить на него посредством исследований. Но я не доверяю своим неосведомленным исследованиям, поэтому я все еще задаю вопрос (что является самым быстрым способом для итерации отдельных символов в строке на С#?).
Иногда я хочу циклически перемещать символы строки один за другим, например, при разборе для вложенных токенов - что-то, что не может быть выполнено с помощью регулярных выражений. Мне интересно, какой самый быстрый способ - перебирать отдельные символы в строке, особенно очень большие строки.
Я проверил себя, и мои результаты ниже. Однако есть много читателей с гораздо более глубокими знаниями о компиляторе .NET CLR и С#, поэтому я не знаю, не хватает ли я чего-то очевидного или если я допустил ошибку в своем тестовом коде. Поэтому я требую вашего коллективного ответа. Если у кого-то есть представление о том, как работает индексатор строк, это было бы очень полезно. (Является ли это языком языка С#, скомпилированным во что-то еще за сценой? Или что-то встроенное в CLR?).
Первый метод, использующий поток, был взят непосредственно из принятого ответа из потока: как создать поток из строки?
Испытания
 longString - это строка в 99,1 миллиона символов, состоящая из 89 экземпляров текстовой версии спецификации языка С#. Результаты показаны для 20 итераций. Там, где есть время запуска (например, для первой итерации неявно созданного массива в методе № 3), я тестировал это отдельно, например, разбивая его из цикла после первой итерации.
Результаты
Из моих тестов кеширование строки в массиве char с использованием метода ToCharArray() является самым быстрым для итерации по всей строке. Метод ToCharArray() является авансом, а последующий доступ к отдельным символам немного быстрее, чем встроенный индексный аксессуар.
                                           milliseconds
                                ---------------------------------
 Method                         Startup  Iteration  Total  StdDev
------------------------------  -------  ---------  -----  ------
 1 index accessor                     0        602    602       3
 2 explicit convert ToCharArray     165        410    582       3
 3 foreach (c in string.ToCharArray)168        455    623       3
 4 StringReader                       0       1150   1150      25
 5 StreamWriter => Stream           405       1940   2345      20
 6 GetBytes() => StreamReader       385       2065   2450      35
 7 GetBytes() => BinaryReader       385       5465   5850      80
 8 foreach (c in string)              0        960    960       4
Обновление: В комментарии @Eric приведены результаты для 100 итераций более обычной строки 1.1 M char (одна копия спецификации С#). Indexer и char все еще быстрее, за ними следует foreach (char в строке), а затем методы потока.
                                           milliseconds
                                ---------------------------------
 Method                         Startup  Iteration  Total  StdDev
------------------------------  -------  ---------  -----  ------
 1 index accessor                     0        6.6    6.6    0.11
 2 explicit convert ToCharArray     2.4        5.0    7.4    0.30
 3 for(c in string.ToCharArray)     2.4        4.7    7.1    0.33
 4 StringReader                       0       14.0   14.0    1.21
 5 StreamWriter => Stream           5.3       21.8   27.1    0.46
 6 GetBytes() => StreamReader       4.4       23.6   28.0    0.65
 7 GetBytes() => BinaryReader       5.0       61.8   66.8    0.79
 8 foreach (c in string)              0       10.3   10.3    0.11     
Используемый код (проверяется отдельно, показывается вместе для краткости)
//1 index accessor
int strLength = longString.Length;
for (int i = 0; i < strLength; i++) { c = longString[i]; }
//2 explicit convert ToCharArray
int strLength = longString.Length;
char[] charArray = longString.ToCharArray();
for (int i = 0; i < strLength; i++) { c = charArray[i]; }
//3 for(c in string.ToCharArray)
foreach (char c in longString.ToCharArray()) { } 
//4 use StringReader
int strLength = longString.Length;
StringReader sr = new StringReader(longString);
for (int i = 0; i < strLength; i++) { c = Convert.ToChar(sr.Read()); }
//5 StreamWriter => StreamReader 
int strLength = longString.Length;
MemoryStream stream = new MemoryStream();
StreamWriter writer = new StreamWriter(stream);
writer.Write(longString);
writer.Flush();
stream.Position = 0;
StreamReader str = new StreamReader(stream);
while (stream.Position < strLength) { c = Convert.ToChar(str.Read()); } 
//6 GetBytes() => StreamReader
int strLength = longString.Length;
MemoryStream stream = new MemoryStream(Encoding.Unicode.GetBytes(longString));
StreamReader str = new StreamReader(stream);
while (stream.Position < strLength) { c = Convert.ToChar(str.Read()); }
//7 GetBytes() => BinaryReader 
int strLength = longString.Length;
MemoryStream stream = new MemoryStream(Encoding.Unicode.GetBytes(longString));
BinaryReader br = new BinaryReader(stream, Encoding.Unicode);
while (stream.Position < strLength) { c = br.ReadChar(); }
//8 foreach (c in string)
foreach (char c in longString) { } 
Принятый ответ:
Я интерпретировал @CodeInChaos и Ben следующие:
fixed (char* pString = longString) {
    char* pChar = pString;
    for (int i = 0; i < strLength; i++) {
        c = *pChar ;
        pChar++;
    }
}
Выполнение для 100 итераций по короткой строке составляло 4,4 мс, с < 0.1 ms st dev.