Для начала скажу, что я согласен с тем, что операторы goto в значительной степени не имеют отношения к конструкциям более высокого уровня в современных языках программирования и не должны использоваться, когда доступна подходящая замена.
Я был повторно- недавно прочитал оригинальную редакцию Code Complete Стива Макконнелла и забыл о своем предложении по поводу распространенной проблемы кодирования.Я читал его много лет назад, когда только начинал, и не думаю, что понял, насколько полезным будет этот рецепт.Проблема кодирования заключается в следующем: при выполнении цикла вам часто нужно выполнить часть цикла, чтобы инициализировать состояние, а затем выполнить цикл с некоторой другой логикой и завершить каждый цикл той же логикой инициализации.Конкретным примером является реализация метода String.Join (разделитель, массив).
Я думаю, что первое, что все решают, - это.Предположим, что метод append определен для добавления аргумента к возвращаемому значению.
bool isFirst = true;
foreach (var element in array)
{
if (!isFirst)
{
append(delimiter);
}
else
{
isFirst = false;
}
append(element);
}
Примечание. Небольшая оптимизация заключается в том, чтобы удалить else и поместить его в конец цикла.Назначение обычно представляет собой одну инструкцию и эквивалентно другому, уменьшает количество базовых блоков на 1 и увеличивает размер базового блока основной части.В результате выполняется условие в каждом цикле, чтобы определить, следует ли добавлять разделитель или нет.
Я также видел и использовал другие способы решения этой общей проблемы цикла.Вы можете выполнить исходный код элемента сначала вне цикла, затем выполнить цикл от второго элемента до конца.Вы также можете изменить логику, чтобы всегда добавлять элемент, а затем разделитель, и после завершения цикла вы можете просто удалить последний добавленный разделитель.
Последнее решение, как правило, является тем, которое я предпочитаю только потому, что ононе дублирует кодЕсли логика последовательности инициализации когда-либо меняется, вам не нужно забывать исправлять ее в двух местах.Однако для этого требуется дополнительная «работа», чтобы выполнить что-то, а затем отменить это, вызывая, по крайней мере, дополнительные циклы процессора, а во многих случаях, например, в нашем примере String.Join, также требуется дополнительная память.прочитайте эту конструкцию
var enumerator = array.GetEnumerator();
if (enumerator.MoveNext())
{
goto start;
do {
append(delimiter);
start:
append(enumerator.Current);
} while (enumerator.MoveNext());
}
Преимущество в том, что вы не получаете дублирующийся код и не получаете никакой дополнительной работы.Вы начинаете свой цикл на полпути к выполнению вашего первого цикла, и это ваша инициализация.Вы ограничены симуляцией других циклов с помощью конструкции do while, но перевод прост, а прочитать его нетрудно.
Итак, теперь вопрос.Я с радостью попытался добавить это в какой-то код, над которым работал, и обнаружил, что он не работает.Прекрасно работает в C, C ++, Basic, но оказывается, что в C # нельзя перейти к метке внутри другой лексической области, которая не является родительской областью.Я был очень разочарован.Так что мне было интересно, как лучше всего справиться с этой очень распространенной проблемой кодирования (я вижу это в основном при генерации строк) в C #?
Чтобы быть более конкретным с требованиями:
- Не дублировать код
- Не выполнять ненужную работу
- Не быть более чем в 2 или 3 раза медленнее, чем другой код
- Быть читаемым
Я думаю, что читаемость - единственное, что может пострадать от рецепта, который я изложил.Однако это не работает в C #, так что же будет лучше?
* Редактировать * Я изменил свои критерии производительности из-за некоторых обсуждений.Производительность, как правило, здесь не является ограничивающим фактором, поэтому более правильной целью должно быть не быть необоснованным и не быть самым быстрым за всю историю.
Причина, по которой мне не нравятся альтернативные реализации, которые я предлагаю, заключается в том, что они либо дублируют код, который оставляет место для изменения одной части, а не другой, либо для той, которую я обычно выбираю, это требует "отмены" операции, которая требует дополнительных усилий и времени для отменить то, что вы только что сделали. В частности, при манипуляциях со строками это обычно приводит к тому, что вы становитесь открытыми из-за одной ошибки или неспособности учесть пустой массив и пытаетесь отменить то, что не произошло.