まず、gotoステートメントは、最新のプログラミング言語の高レベルの構造によってほとんど無関係になり、適切な代替が利用できる場合は使用しないことに同意します。
私は最近、スティーブマコネルのコードコンプリートのオリジナル版を読み直していて、一般的なコーディングの問題についての彼の提案を忘れていました。何年も前に最初に読んだことがありますが、レシピがどれほど役立つかはわかりませんでした。コーディングの問題は次のとおりです。ループを実行する場合、状態を初期化するためにループの一部を実行してから、他のロジックでループを実行し、同じ初期化ロジックで各ループを終了する必要があります。具体的な例は、String.Join(delimiter、array)メソッドの実装です。
みんなが最初に問題に取り組むのはこれだと思います。引数を戻り値に追加するためにappendメソッドが定義されていると仮定します。
bool isFirst = true;
foreach (var element in array)
{
if (!isFirst)
{
append(delimiter);
}
else
{
isFirst = false;
}
append(element);
}
注:これに対するわずかな最適化は、elseを削除して、ループの最後に配置することです。割り当ては通常、単一の命令であり、elseと同等であり、基本ブロックの数を1つ減らし、主要部分の基本ブロックサイズを増やします。その結果、各ループで条件を実行して、区切り文字を追加する必要があるかどうかを判断します。
私はまた、この一般的なループの問題に対処するための他の見解を見て使用しました。最初にループの外側で最初の要素コードを実行してから、2番目の要素から最後までループを実行できます。ロジックを変更して、常に要素と区切り文字を追加することもできます。ループが完了したら、最後に追加した区切り文字を削除するだけです。
後者の解決策は、コードを複製しないという理由だけで私が好む解決策になる傾向があります。初期化シーケンスのロジックが変更された場合でも、2か所で修正することを覚えておく必要はありません。ただし、何かを実行してから元に戻すには余分な「作業」が必要であり、少なくとも余分なCPUサイクルが発生し、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#では機能しないので、次善の策は何ですか?
*編集* いくつかの議論のためにパフォーマンス基準を変更しました。ここでは通常、パフォーマンスが制限要因ではないため、より正確な目標は、不合理にならないようにすること、これまでで最速にならないようにすることです。
私が提案する代替の実装が嫌いな理由は、コードを複製して一方の部分を変更する余地を残しているため、または私が一般的に選択する実装では、操作を「元に戻す」必要があり、その操作を元に戻すには余分な思考と時間が必要になるためです。あなたがやったこと。特に文字列操作では、これにより通常、1つのエラーが発生したり、空の配列の説明に失敗したり、発生しなかった何かを元に戻そうとしたりすることができます。