上記の説明が正しければ、.Net BCL でリフレクターを使用すると、頻繁に使用されるのはなぜですか?
編集: 言い換えさせてください: 人間またはコンパイラによって書かれたリフレクターに表示されるすべてのGO-TOですか?
上記の説明が正しければ、.Net BCL でリフレクターを使用すると、頻繁に使用されるのはなぜですか?
編集: 言い換えさせてください: 人間またはコンパイラによって書かれたリフレクターに表示されるすべてのGO-TOですか?
Goto に関するウィキペディアの記事からの次の抜粋は、ここで特に関連があると思います。
おそらく、GOTO に対する最も有名な批判は、1968 年に Edsger Dijkstra が書いた「Go To ステートメントは有害であると考えられる」という手紙です。その手紙の中で、ダイクストラは、プログラム (特にループを含むもの) の正確性を分析および検証するタスクを複雑にするため、制限のない GOTO ステートメントを高水準言語から廃止する必要があると主張しました。Donald Knuth の『構造化プログラミング with go to ステートメント』では、別の視点が提示されています。これは、多くの一般的なプログラミング タスクを分析し、そのうちのいくつかで GOTO が使用するのに最適な言語構造であることを発見しています。
そのため、一方ではEdsger Dijkstra (信じられないほど才能のあるコンピューター科学者)が、ステートメントの使用に反対GOTO
し、具体的には、コードの記述方法が構造化されていないという理由で、ステートメントの過度の使用に反対しています。GOTO
一方、Donald KnuthGOTO
(もう 1 人の信じられないほど才能のあるコンピューター科学者) は、 を使用すること、特に慎重に使用することは、特定のプログラム コードに対して実際に「最良」かつ最適な構造である可能性があると主張しています。
最終的には、私見ですが、どちらの男性も正しいと思います。ステートメントを使いすぎると、GOTO
コードの一部が読みにくくなり、構造化 されなくなるという点で、ダイクストラは正しいです。これは、コンピューター プログラミングを純粋に理論的な観点から見ると確かに当てはまります。
ただし、実際的なアプローチを取らなければならない「現実の世界」では、GOTO
ステートメントを賢く使用すると、実際に使用する言語構造の最良の選択になる可能性があるため、クヌースも正しいです。
上記は実際には正しくありません。これは、goto が使用されていた唯一のフロー制御構造であった当時、Dijkstra によって使用された論争的なデバイスでした。実際、Knuth の古典的な論文「Goto を使用した構造化プログラミング」(記憶によるタイトル) など、何人かが反論を作成しています。また、goto が「構造化された」代替手段よりも明確なコード (IMHO) を生成できる状況 (エラー処理、ステート マシン) もあります。
これらgoto
は、特に列挙子内でコンパイラによって生成されることが非常に多いです。コンパイラは、彼女が何をしているかを常に知っています。
を使用する必要がある場合goto
は、それが唯一のオプションであることを確認する必要があります。ほとんどの場合、より良い解決策があることがわかります。
それ以外では、goto
入れ子になったループを使用する場合など、 の使用が正当化される例はほとんどありません。繰り返しますが、この場合でも他のオプションがあります。関数内の内側のループを分割し、代わりに return ステートメントを使用できます。追加のメソッド呼び出しが本当にコストがかかりすぎるかどうかをよく調べる必要があります。
あなたの編集に応じて:
いいえ、すべての goto がコンパイラによって生成されるわけではありませんが、それらの多くは、コンパイラによって生成されるステート マシン (列挙子)、switch case ステートメント、または最適化された if else 構造から生じます。それがコンパイラによるものなのか、元の開発者によるものなのかを判断できる例は、ほんのわずかです。関数/クラス名を見ることで良いヒントを得ることができます。コンパイラは、コードとの名前の衝突を避けるために「禁止された」名前を生成します。すべてが正常に見え、コードが最適化または難読化されていない場合は、おそらく goto の使用が意図されています。
Reflector に表示されているコードは逆アセンブリであることを覚えておいてください。Reflector は、コンパイルされたバイト コードを見て、元のソース コードをつなぎ合わせようとしています。
goto
そのため、 s に対する規則が高レベル コードに適用されることを覚えておく必要があります。を置き換えるために使用されるすべての構成要素goto
( for
、while
、など) はすべてbreak
、switch
JMP を使用してコードにコンパイルされます。
つまり、Reflector はコードを次のように認識します。
A:
if !(a > b)
goto B;
DoStuff();
goto A;
B: ...
そして、それが実際には次のようにコード化されていることを認識しなければなりません:
while (a > b)
DoStuff();
コードが複雑すぎてパターンを認識できない場合があります。
Go To
ステートメント自体は有害ではなく、時には非常に便利です。有害なのは、コード内の不適切な場所に配置する傾向があるユーザーです。
アセンブリ コードにコンパイルすると、すべてのコントロールが構造化され、(無) 条件付きジャンプに変換されます。ただし、オプティマイザが強力すぎる可能性があり、ジャンプ パターンが対応する制御構造を逆アセンブラが識別できない場合、常に正しいステートメント、すなわちgoto label;
が発行されます。
これは、 の無害性とは何の関係もありませんgoto
。
たとえば、二重ループまたは多くのネストされたループについてはどうですか。
foreach (KeyValuePair<DateTime, ChangedValues> changedValForDate in _changedValForDates)
{
foreach (KeyValuePair<string, int> TypVal in changedValForDate.Value.TypeVales)
{
RefreshProgress("Daten werden geändert...", count++, false);
if (IsProgressCanceled)
{
goto TheEnd; //I like goto :)
}
}
}
TheEnd:
この場合、break を使用して次のことを行う必要があることを考慮する必要があります。
foreach(KeyValuePair<DateTime, ChangedValues> changedValForDate in _changedValForDates)
{
foreach (KeyValuePair<string, int> TypVal in changedValForDate.Value.TypeVales)
{
RefreshProgress("Daten werden geändert...", count++, false);
if (IsProgressCanceled)
{
break; //I miss goto :|
}
}
if (IsProgressCanceled)
{
break; //I really miss goto now :|
}//waaaAA !! so many brakets, I hate'm
}
一般的なルールは、使用する必要がないということですgoto
。他の規則と同様、もちろん例外はありますが、例外はほとんどありません。
goto
コマンドは麻薬のようなものです。特別な時だけ限られた量で使うならそれでいい。常に使いすぎると、人生が台無しになります。
Reflector を使用してコードを見ていると、実際のコードが表示されません。コンパイラが元のコードから生成したものから再作成されたコードが表示されています。再作成されたコードにa が表示された場合、元のコードに a があったかどうかはgoto
定かではありませんgoto
。フローを制御するためのより構造化されたコマンド ( abreak
または acontinue
と同じ方法でコンパイラによって実装されたa など) が存在する可能性があるgoto
ため、Reflector は違いを認識できません。
逆コンパイルされたコードでは、表示される事実上すべてgoto
の が合成になります。それらについて心配する必要はありません。それらは、コードが低レベルでどのように表現されているかの成果物です。
それらを独自のコードに入れる正当な理由については? 私が考えることができる主なものは、あなたが使用している言語が、あなたが取り組んでいる問題に適した制御構造を提供していない場所です。カスタム制御フロー システムを簡単に作成できる言語には、通常、まったくありませんgoto
。それらをまったく使用しないようにすることも常に可能ですが、任意に複雑なコードを while ループに再配置し、制御変数のバッテリー全体を使用して多くの条件を設定すると、実際にはコードがさらに不明瞭になります (また、速度も低下します; コンパイラは通常、そのような複雑さを区別するほどスマートではありません)。プログラミングの主な目標は、コンピューターとそれを読む人々の両方にとって明確なプログラムの記述を作成することです。
goto
有害であると見なされます (人間が使用することはできますが、コンピューターにとっては問題ありません)。
私たち(人間)がどれほど狂ったように使用してもgoto
、コンパイラは常にコードの読み方を知っているからです。
私を信じてください...
s を含む他のコードを読むgoto
のは難しいです。
s を含む独自のコードを読むのgoto
は難しくなります。
そのため、低レベル (機械語) で使用され、高レベル (C#、Python などの人間の言語) では使用されていません ;)
終了アクションを実行したい場合、goto を使用することがあります。
static void DoAction(params int[] args)
{
foreach (int arg in args)
{
Console.WriteLine(arg);
if (arg == 93) goto exit;
}
//edit:
if (args.Length > 3) goto exit;
//Do another gazillion actions you might wanna skip.
//etc.
//etc.
exit:
Console.Write("Delete resource or whatever");
}
したがってreturn
、 を押す代わりに、単に終了するのではなく、スニペットのさまざまな場所から参照できる別の最終アクションを実行する最後の行に送信します。
「C は、無限に悪用可能な goto ステートメントと、分岐先のラベルを提供します。正式には、goto は決して必要ありません。実際には、goto なしでコードを書くことはほとんどの場合簡単です。この本では goto を使用していません。」
-- K&R (第 2 版) : 65 ページ
上記のように使いすぎなければ、GOTO は便利です。Microsoft は、.NET Framework 自体のいくつかのインスタンスでそれを使用しています。
害があるかどうかは、それぞれの好き嫌いの問題です。私は個人的にそれらが好きではなく、コードの保守性を試みるため、非常に非生産的だと感じています。
さて、1 つは goto がコードの読み取りにどのように影響するか、もう 1 つはジッターが検出されたときにどのように実行されるかです。Eric Lippert のブログから、引用したいと思います。
最初にパスを実行して、ループを goto とラベルに変換します。
したがって、実際には、コンパイラは IL を出力しながら、各フロー制御構造を goto/label パターンにかなり変換します。リフレクターはアセンブリの IL を読み取ると、パターンを認識し、適切なフロー制御構造に変換します。
場合によっては、発行されたコードが複雑すぎてリフレクターが理解できない場合、読み取っている IL に相当する、ラベルと goto を使用する C# コードが表示されるだけです。これは、たとえばandステートメントIEnumerable<T>
を使用してメソッドを実装する場合に当てはまります。これらの種類のメソッドは、基礎となるステート マシンを使用してインターフェイスを実装する独自のクラスに変換されます。BCL では、このようなケースがたくさん見つかると思います。yield return
yield break
IEnumerable<T>