В сочетании голубь + ответы Константина ...
static IEnumerable<string> WholeChunks(string str, int chunkSize) {
for (int i = 0; i < str.Length; i += chunkSize)
yield return str.Substring(i, chunkSize);
}
Это будет работать для всех строк, которые могут быть разбиты на целое число фрагментов, и в противном случае выдает исключение.
Если вы хотите поддерживать строки любой длины, вы можете использовать следующий код:
static IEnumerable<string> ChunksUpto(string str, int maxChunkSize) {
for (int i = 0; i < str.Length; i += maxChunkSize)
yield return str.Substring(i, Math.Min(maxChunkSize, str.Length-i));
}
Однако ОП явно заявил, что он не нуждается в этом; это немного длиннее и труднее читать, немного медленнее. В духе KISS и YAGNI я бы выбрал первый вариант: возможно, это наиболее эффективная реализация, и она очень короткая, удобочитаемая и, что важно, создает исключение для несоответствующего ввода.