31

.NETベースライブラリのリフレクターでこのコードを見つけました...

    if (this._PasswordStrengthRegularExpression != null)
    {
        this._PasswordStrengthRegularExpression = this._PasswordStrengthRegularExpression.Trim();
        if (this._PasswordStrengthRegularExpression.Length == 0)
        {
            goto Label_016C;
        }
        try
        {
            new Regex(this._PasswordStrengthRegularExpression);
            goto Label_016C;
        }
        catch (ArgumentException exception)
        {
            throw new ProviderException(exception.Message, exception);
        }
    }
    this._PasswordStrengthRegularExpression = string.Empty;
Label_016C:
    ... //Other stuff

「永遠に地獄に亡命することを恐れて、後藤を使ってはならない」という言葉をすべて聞いたことがあります。私は常にMSコーダーをかなり高く評価しており、彼らの決定のすべてに同意したわけではないかもしれませんが、私は常に彼らの推論を尊重していました。

だから-私が行方不明になっているこのようなコードの正当な理由はありますか?このコード抽出は、不適切な開発者によってまとめられたものですか?または、.NETリフレクターが不正確なコードを返していますか?

私は正当な理由があることを望んでいます、そして私はそれをやみくもに見逃しています。

皆さんのご意見ありがとうございます

4

19 に答える 19

44

リフレクターは完璧ではありません。このメソッドの実際のコードは、リファレンスソースから入手できます。ndp \ fx \ src \ xsp \ system \ web \ security\admembershipprovider.csにあります。

        if( passwordStrengthRegularExpression != null )
        { 
            passwordStrengthRegularExpression = passwordStrengthRegularExpression.Trim();
            if( passwordStrengthRegularExpression.Length != 0 ) 
            { 
                try
                { 
                    Regex regex = new Regex( passwordStrengthRegularExpression );
                }
                catch( ArgumentException e )
                { 
                    throw new ProviderException( e.Message, e );
                } 
            } 
        }
        else 
        {
            passwordStrengthRegularExpression = string.Empty;
        }

最後のelse句の検出に失敗し、gotoで補正したことに注意してください。ほぼ確実に、if()ステートメント内のtry/catchブロックによってトリップされます。

明らかに、逆コンパイルされたバージョンではなく、実際のソースコードを優先することをお勧めします。コメント自体は非常に役立ち、ソースが正確であることを期待できます。まあ、ほとんど正確ですが、Microsoftプログラマーの名前を削除したバグのある後処理ツールによるいくつかの小さな損傷があります。識別子がダッシュに置き換えられ、コードが2回繰り返されることがあります。ここからソースをダウンロードできます。

于 2010-03-30T02:09:29.203 に答える
12

それはおそらくソースコードにはないでしょう、それは分解されたコードがどのように見えるかです。

于 2010-03-30T01:37:10.957 に答える
12

ネストされたループから抜け出すためにgotoが使用されているのを見てきました。

Objective-Cでネストされた2つのforループから抜け出すにはどうすればよいですか?

私はそれをそのように使うことに何も悪いことは見ていません。

于 2010-03-30T01:48:27.317 に答える
11

.NETでのgotoの有効な使用法はいくつかあります(具体的にはC#)。

Switchステートメントのフォールスルーセマンティクスのシミュレーション

C ++のバックグラウンドから来たものは、明示的にbreakで終了しない限り、ケースからケースへと自動的にフォールスルーするswitchステートメントの記述に慣れています。C#の場合、些細な(空の)ケースのみがフォールスルーします。

たとえば、C++では

int i = 1;
switch (i)
{
case 1:
  printf ("Case 1\r\n");
case 2:
  printf ("Case 2\r\n");
default:
  printf ("Default Case\r\n");
  break;
}

このC++コードでは、出力は次のとおりです。

ケース1
ケース2
デフォルトの場合

同様のC#コードは次のとおりです。

int i = 1;
switch (i)
{
case 1:
  Console.Writeline ("Case 1");
case 2:
  Console.Writeline ("Case 2");
default:
  Console.Writeline ("Default Case");
  break;
}

書かれているように、これはコンパイルされません。次のようなコンパイルエラーがいくつかあります。

コントロールは、あるケースラベル(「ケース1:」)から別のケースラベルにフォールスルーすることはできません

gotoステートメントを追加すると機能します。

int i = 1;
switch (i)
{
case 1:
    Console.WriteLine ("Case 1");
    goto case 2;
case 2:
    Console.WriteLine("Case 2");
    goto default;
default:
    Console.WriteLine("Default Case");
    break;
}

... C#で使用する他の便利なgotoは...

無限ループと展開された再帰

あまり役に立たないため、ここでは詳しく説明しませんがwhile(true)、aで明示的に終了するか、ステートメントbreakで再実行される構造を使用して、無限ループを作成することがあります。continueこれは、再帰的なメソッド呼び出しをシミュレートしようとしているが、再帰の潜在的な範囲を制御できない場合に発生する可能性があります。

明らかに、while(true)それをループにリファクタリングしたり、別のメソッドにリファクタリングしたりできますが、ラベルとgotoステートメントを使用することもできます。

このgotoの使用については議論の余地がありますが、非常にまれな状況でのオプションとして覚えておく価値のあるものです。

于 2010-07-02T02:20:34.337 に答える
8

私はgotosに夢中ではありませんが、それらが決して有効ではないと言うのはばかげています。

私は1回使用して、特に厄介なコードの欠陥を修正しました。コードをリファクタリングしてテストすることは、時間の制約を考えると実用的ではなかったでしょう。

さらに、コーディングが不十分で、gotoが無害に見える条件付き構造を見たことがありませんか?

于 2010-03-30T05:22:42.667 に答える
5

GOTOを使用すると、パフォーマンスを向上させて再帰を実行できます。メンテナンスは非常に困難ですが、これらの追加のサイクルが必要な場合は、メンテナンスの負担を喜んで支払うことができます。

簡単な例を次に示します。結果は次のとおりです。

class Program
{
    // Calculate (20!) 1 million times using both methods.
    static void Main(string[] args)
    {
        Stopwatch sw = Stopwatch.StartNew();
        Int64 result = 0;
        for (int i = 0; i < 1000000; i++)
            result += FactR(20);
        Console.WriteLine("Recursive Time: " + sw.ElapsedMilliseconds);

        sw = Stopwatch.StartNew();
        result = 0;
        for (int i = 0; i < 1000000; i++)
            result += FactG(20);
        Console.WriteLine("Goto Time: " + sw.ElapsedMilliseconds);
        Console.ReadLine();
    }

    // Recursive Factorial
    static Int64 FactR(Int64 i)
    {
        if (i <= 1)
            return 1;
        return i * FactR(i - 1);
    }

    // Recursive Factorial (using GOTO)
    static Int64 FactG(Int64 i)
    {
        Int64 result = 1;

    Loop:
        if (i <= 1)
            return result;

        result *= i;
        i--;
        goto Loop;
    }

これが私のマシンで得られる結果です:

 Recursive Time: 820
 Goto Time: 259
于 2010-07-02T17:28:37.493 に答える
4

あなたはリフレクターコードを見るべきではありません。

分解されたILを見ると、あちこちにgotoが表示されます。本質的に、私たちが使用するすべてのループと他の制御構造は、とにかくgotoに変換されます。それはコード内でそれらを構造に変換することによって、より読みやすく、保守しやすくなるということです。

ちなみに、あなたが投稿したコードはgotoを使用するのに適した場所ではないと思います。そして、私は1つを考えるのに苦労しています。

于 2010-03-30T01:34:48.153 に答える
3

作成およびレビューされた.NETコードの多くの行で、Gotoの有効なケースを見たことがありません。

finallyブロックを使用した構造化例外処理をサポートしない言語(PASCAL-構造化プログラミング言語の祖父と古典的なCが頭に浮かぶ)では、GOTOを戦術的に使用すると、実行に使用したときにコードをはるかに理解しやすくなる可能性がありますネストされたループ内で実行が終了したときのクリーンアップ(複数のループ終了条件を正しく設定するのではなく)。そのため、当時も個人的に後藤を使用していませんでした(恐らく「永遠に地獄に亡命する」ことを恐れて)。

于 2010-03-30T01:32:54.270 に答える
3

Gotoは、パーサーやレクサーを作成するときによく役立ちます。

于 2010-03-30T02:04:00.367 に答える
2

いいえ、使用する正当な理由はありませんgotogoto私は1981年にステートメントを最後にコーディングしましたが、それ以来、その特定の構成を見逃していません。

于 2010-03-30T01:47:14.057 に答える
2

これらの優れた有効なものすべてに加えて、逆アセンブルされたコードを見るときは、開発者がこれらのアセンブリで難読化ツールを使用している可能性があることに注意してください。難読化の1つの手法は、ILにランダムなgotoを追加することです。

于 2010-03-30T01:50:16.113 に答える
2

状態図を見てください。使用するのに最適なコード構造が、意図を最も直接的かつ明確に表現するものであると思われる場合は、これらの状態遷移のすべてをgotoとしてコーディングする必要があります。

ただし、これは現実の世界では崩壊する傾向があります。最初の問題は、マシンを停止し、他のコードに戻って、後でマシンを再開する必要があることです。つまり、これらの遷移はすべて、スイッチの正しい状態を識別するために使用される状態変数への変更になる傾向があります。 /caseステートメント。これは、実際にはgotoを非表示にして遅延させる方法にすぎません。状態変数への書き込みは、プログラムカウンターレジスタへの書き込みと大差ありません。これは、「goto there-しかし、今ではなく、後で」を実装するための単なる方法です。

ただし、gotoが、ある種の状態モデルで何が起こっているかを表現するのにうまく機能する場合があります。たとえば、医師が時々使用する診断フローチャートの1つであると思います。トランジションにgotoを使用せずに、これらの1つをプログラムとして実装すると、コードの意図を暗号化することで、実際には自分の生活を困難にすることになります。

まさに、最も一般的なケースは手書きのコードではない可能性が高いということです。さまざまな種類の状態モデル(意思決定処理、正規文法解析など)で遷移のgotoステートメントを生成するコードジェネレーターを作成しましたが、最後に手書きコードでgotoを使用したのは覚えていません。

于 2010-03-30T01:55:20.610 に答える
1

この点に関して:

だから-私が行方不明になっているこのようなコードの正当な理由はありますか?このコード抽出は、くだらない開発者によってまとめられただけですか?または、.NETリフレクターが不正確なコードを返していますか?

私はこれらが唯一の3つの可能性であるという前提に同意しません。

他の多くの人が示唆しているように、これは単にライブラリ内の実際のソースコードを正確に反映したものではないというのは本当かもしれません。とにかく、私たちは皆、次の目的でコードを「汚い方法」で書いたことについて罪を犯してきました(とにかく、私はそうです)。

  1. 機能をすばやく実装する
  2. バグをすばやく修正する
  3. わずかなパフォーマンスの向上を絞り出します(正当な理由がある場合もあれば、それほど多くない場合もあります)
  4. 当時理にかなっていた他の理由

それは誰かを「くだらない開発者」にするわけではありません。「gotoを使用してはならない」などのほとんどのガイドラインは、主に開発者を自分自身から保護するために用意されています。良い開発者と悪い開発者を区別するための鍵として扱われるべきではありません。

例えとして、私たちの多くが小学校の英語で教えられている単純な規則を考えてみましょう。前置詞で文を終わらせないでください。これは本当のルールではありません; 「車はどこ?」などと言われないようにするためのガイドラインです。この事実を理解することが重要です。ガイドラインではなく実際のルールのように扱い始めると、「何を恐れているのか」のような完全に良い文章で人々を「修正」していることに気付くでしょう。

このことを念頭に置いて、他の開発者がを使用したために「くだらない」と呼んだ開発者には注意が必要gotoです。

私は確かにgotoそれ自体を擁護しようとはしていません-その使用は決して無能を示すものではないと主張しているだけです。

于 2010-03-30T04:51:35.137 に答える
1

他の人が示しているように、リフレクターに表示されるコードは、必然的にフレームワークで記述されたコードです。コンパイラーとオプティマイザーは、コードによって実行される実際の作業を変更しない限り、コードを同様の方法で機能するものに変更できます。また、コンパイラーはすべてのブランチとループをgoto(ILのブランチ、またはアセンブリーのジャンプ)として実装することにも注意してください。リリースモードが実行され、コンパイラーがコードを機能的に同じ最も単純な形式に最適化しようとする場合ソース。

リリース用にコンパイルすると、すべて100%同じILにコンパイルされるさまざまなループ手法の例があります。 他の回答を参照してください

(現在は見つかりませんが、Eric Lippertは、C#コンパイラがコードを処理する方法についてのメモを投稿しました。彼の指摘の1つは、すべてのループをgotoに変更する方法です。)

そうは言っても、後藤は問題ありません。より良いループ構造がある場合は、それを使用してください。しかし、場合によっては、for、foreach、while、do / whileから絞り出すことができるものが少し必要になることがありますが、メソッド呼び出しから生じる追加の混乱や苦痛は望ましくありません(ネストされたを変換するために5行以上を無駄にする理由再帰的メソッド。)

于 2010-03-30T11:34:38.740 に答える
0

有効なケースが1つあります。再帰的なプロシージャ呼び出しをシミュレートして非再帰的なコードで返そうとしている場合、または同様のことを実行しようとしている場合です(この種の要件はPrologインタープリターでも発生します)。ただし、一般に、チェスプログラムや言語インタープリターなどのマイクロ最適化が必要な場合を除いて、通常のプロシージャスタックを使用し、関数/プロシージャ呼び出しを使用する方がはるかに優れています。

于 2010-03-30T01:36:50.453 に答える
0

gotoは、少なくともCのような言語でのクリーンアップに完全に有効であり、例外の概念をある程度シミュレートします。.NETにはこのようなものを処理するためのより良い方法があると確信しているので、gotoは時代遅れでエラーが発生しやすいだけです。

于 2010-03-30T02:02:40.953 に答える
0

私はそのコードが好きではありません。

正規表現をメンバーに保存し、設定時に検証して、読み取るときにロジックが不要になるようにします。

于 2010-03-30T02:12:04.097 に答える
0

gotoこれは最良の例ではないかもしれませんが、非常に便利な場合を示しています。

private IDynamic ToExponential(Engine engine, Args args)
{
    var x = engine.Context.ThisBinding.ToNumberPrimitive().Value;

    if (double.IsNaN(x))
    {
        return new StringPrimitive("NaN");
    }

    var s = "";

    if (x < 0)
    {
        s = "-";
        x = -x;
    }

    if (double.IsPositiveInfinity(x))
    {
        return new StringPrimitive(s + "Infinity");
    }

    var f = args[0].ToNumberPrimitive().Value;
    if (f < 0D || f > 20D)
    {
        throw new Exception("RangeError");
    }

    var m = "";
    var c = "";
    var d = "";
    var e = 0D;
    var n = 0D;

    if (x == 0D)
    {
        f = 0D;
        m = m.PadLeft((int)(f + 1D), '0');
        e = 0;
    }
    else
    {
        if (!args[0].IsUndefined) // fractionDigits is supplied
        {
            var lower = (int)Math.Pow(10, f);
            var upper = (int)Math.Pow(10, f + 1D);
            var min = 0 - 0.0001;
            var max = 0 + 0.0001; 

            for (int i = lower; i < upper; i++)
            {
                for (int j = (int)f;; --j)
                {
                    var result = i * Math.Pow(10, j - f) - x;
                    if (result > min && result < max)
                    {
                        n = i;
                        e = j;
                        goto Complete;
                    }
                    if (result <= 0)
                    {
                        break;
                    }
                }

                for (int j = (int)f + 1; ; j++)
                {
                    var result = i * Math.Pow(10, j - f) - x;
                    if (result > min && result < max)
                    {
                        n = i;
                        e = j;
                        goto Complete;
                    }
                    if (result >= 0)
                    {
                        break;
                    }
                }
            }
        }
        else
        {
            var min = x - 0.0001;
            var max = x + 0.0001; 

            // Scan for f where f >= 0
            for (int i = 0;; i++)
            {
                // 10 ^ f <= n < 10 ^ (f + 1)
                var lower = (int)Math.Pow(10, i);
                var upper = (int)Math.Pow(10, i + 1D);
                for (int j = lower; j < upper; j++)
                {
                    // n is not divisible by 10
                    if (j % 10 == 0)
                    {
                        continue;
                    }

                    // n must have f + 1 digits
                    var digits = 0;
                    var state = j;
                    while (state > 0)
                    {
                        state /= 10;
                        digits++;
                    }
                    if (digits != i + 1)
                    {
                        continue;
                    }

                    // Scan for e in both directions
                    for (int k = (int)i; ; --k)
                    {
                        var result = j * Math.Pow(10, k - i);
                        if (result > min && result < max)
                        {
                            f = i;
                            n = j;
                            e = k;
                            goto Complete;
                        }
                        if (result <= i)
                        {
                            break;
                        }
                    }
                    for (int k = (int)i + 1; ; k++)
                    {
                        var result = i * Math.Pow(10, k - i);
                        if (result > min && result < max)
                        {
                            f = i;
                            n = j;
                            e = k;
                            goto Complete;
                        }
                        if (result >= i)
                        {
                            break;
                        }
                    }
                }
            }
        }

    Complete:

        m = n.ToString("G");
    }

    if (f != 0D)
    {
        m = m[0] + "." + m.Substring(1);
    }

    if (e == 0D)
    {
        c = "+";
        d = "0";
    }
    else
    {
        if (e > 0D)
        {
            c = "+";
        }
        else
        {
            c = "-";
            e = -e;
        }
        d = e.ToString("G");
    }

    m = m + "e" + c + d;
    return new StringPrimitive(s + m);
}
于 2010-07-02T03:05:30.930 に答える
-1

FORTRANを書いたとき、GOTOでコーディングしたことすらありませんでした。

私はそれを使う必要はありませんでした。現代語がユーザーにそのようなことを要求する理由がわかりません。はっきりと「ノー」と言います。

于 2010-03-30T02:01:51.057 に答える