10

次のコードは、メッセージのバッチ処理ルーチンの概念実証です。ペストのように避けgotoて、このコードを書き直しますか? それとも、これを実現するための表現力豊かな方法だと思いますgotoか?

書き直す場合は、コードを投稿してください...

var queue = new Queue<TraceItem>(this.batch);
while (this.connected)
{
    byte[] buffer = null;
    try
    {
        socket.Recv(out buffer);
    }
    catch
    {
        // ignore the exception we get when the socket is shut down from another thread
        // the connected flag will be set to false and we'll break the loop
    }

HaveAnotherMessage:
    if (buffer != null)
    {
        try
        {
            var item = TraceItemSerializer.FromBytes(buffer);
            if (item != null)
            {
                queue.Enqueue(item);

                buffer = null;
                if (queue.Count < this.batch && socket.Recv(out buffer, ZMQ.NOBLOCK))
                {
                    goto HaveAnotherMessage;
                }
            }
        }
        catch (Exception ex)
        {
            this.ReceiverPerformanceCounter.IncrementDiagnosticExceptions();
            this.tracer.TraceException(TraceEventType.Error, 0, ex);
        }
    }

    // queue processing code
}
4

10 に答える 10

45

後藤は厄介な状況にあなたを連れて行きます

「goto」についての私の考えをかなり要約しています。

Goto は、多くの理由でプログラミングの悪い習慣です。それらの主なものは、それにはほとんど理由がないということです。誰かがdo..whileループを投稿しました。それを使用してください。a を使用しbooleanて続行するかどうかを確認します。while ループを使用します。JMPGoto はインタープリター言語用であり、アセンブラーの時代 (誰か?)へのコールバック用です。何らかの理由で高級言語を使用しています。あなたと他の人があなたのコードを見て迷子にならないように。


gotoこの回答を最新の状態に保つために、と ブレース エラーの組み合わせにより、iOS および OS X で主要な SSL バグが発生したことを指摘したいと思います。

于 2010-07-07T18:39:40.050 に答える
18

goto を do-while に置き換えるか、現在の「常に 1 回実行する」機能が必要ない場合は単に while ループに置き換えます。

var queue = new Queue<TraceItem>(this.batch);
while (this.connected)
{
    byte[] buffer = null;
    try
    {
        socket.Recv(out buffer);
    }
    catch
    {
        // ignore the exception we get when the socket is shut down from another thread
        // the connected flag will be set to false and we'll break the loop
    }

    do {
        if (buffer != null)
        {
            try
            {
                var item = TraceItemSerializer.FromBytes(buffer);
                if (item != null)
                {
                    queue.Enqueue(item);
                    buffer = null;
                }
            }
            catch (Exception ex)
            {
                this.ReceiverPerformanceCounter.IncrementDiagnosticExceptions();
                this.tracer.TraceException(TraceEventType.Error, 0, ex);
            }
        }
    } while(queue.Count < this.batch && socket.Recv(out buffer, ZMQ.NOBLOCK))

    // queue processing code
}
于 2010-07-07T18:41:37.263 に答える
18

この状況で GOTO を取り除くのは驚くほど簡単で、私は泣きそうになります。

var queue = new Queue<TraceItem>(this.batch);
while (this.connected)
{
    byte[] buffer = null;
    try
    {
        socket.Recv(out buffer);
    }
    catch
    {
        // ignore the exception we get when the socket is shut down from another thread
        // the connected flag will be set to false and we'll break the loop
    }
    bool hasAnotherMessage = true
    while(hasAnotherMessage)
    {
        hasAnotherMessage = false;
        if (buffer != null)
        {
            try
            {
                var item = TraceItemSerializer.FromBytes(buffer);
                if (item != null)
                {
                    queue.Enqueue(item);

                    buffer = null;
                    if (queue.Count < this.batch && socket.Recv(out buffer, ZMQ.NOBLOCK))
                    {
                        hasAnotherMessage = true;
                    }
                }
            }
            catch (Exception ex)
            {
                this.ReceiverPerformanceCounter.IncrementDiagnosticExceptions();
                this.tracer.TraceException(TraceEventType.Error, 0, ex);
            }
        }
    }
    // queue processing code
}
于 2010-07-07T18:42:50.333 に答える
5

goto は直感的に少し読みやすいと思います...しかし、それを避けたい場合は、コードをwhile(true)ループでスローし、ループbreakの最後に通常のステートメントを配置するだけでよいと思います反復。そして、ステートメントgotoに置き換えることができます。continue

goto最終的には、少なくとも私の経験では、ステートメントを使用する代わりに、ループやその他の制御フロー構造の読み書きを学ぶだけです。

于 2010-07-07T18:38:38.977 に答える
4

Josh K の投稿に関連していますが、コメントでコードを使用できないため、ここに書きます。

私は正当な理由を考えることができます: 何かを見つけるためにいくつかの n 次元構造をトラバースしている間。n=3 の例 //...

for (int i = 0; i < X; i++)
    for (int j = 0; j < Y; j++)
        for (int k = 0; k < Z; k++)
            if ( array[i][j][k] == someValue )
            {
                //DO STUFF
                goto ENDFOR; //Already found my value, let's get out
            }
ENDFOR: ;
//MORE CODE HERE...

「n」whileとブール値を使用して続行する必要があるかどうかを確認できることは知っています..または、そのn次元配列を1次元だけにマップし、1つのwhileだけを使用する関数を作成できますが、ネストされていると信じていますより読みやすく。

ところで、全員が goto を使用する必要があると言っているわけではありませんが、この特定の状況では、先ほど述べた方法で使用します。

于 2010-07-08T03:21:48.250 に答える
3

このようなものにリファクタリングできます。

while (queue.Count < this.batch && buffer != null)
{
    try
    {
        var item = TraceItemSerializer.FromBytes(buffer);
        buffer = null;
        if (item != null)
        {
            queue.Enqueue(item);
            socket.Recv(out buffer, ZMQ.NOBLOCK)
        }
    }
    catch (Exception ex)
    {
        this.ReceiverPerformanceCounter.IncrementDiagnosticExceptions();
        this.tracer.TraceException(TraceEventType.Error, 0, ex);
    }
}
于 2010-07-07T18:42:43.630 に答える
0

「HaveAnotherMessage」を、バッファを取り、それ自体を再帰的に呼び出すことができるメソッドにラップします。これは、これを修正する最も簡単な方法のようです。

于 2010-07-07T18:40:30.640 に答える
0

この場合、goto を避け、リファクタリングします。私の意見では、この方法は長すぎます。

于 2010-07-07T18:41:08.510 に答える
0

うーん、try ブロックから出たいのかよくわかりません。私はそれについて100%確信しているわけではありませんが、それは安全なことではないと確信しています。それはとても安全に見えません...

于 2010-07-07T18:38:42.860 に答える
0

あなたの方法は大きすぎると思います。エラー処理、メッセージ検索、メッセージ処理など、さまざまなレベルの抽象化が混在しています。

別のメソッドでリファクタリングすると、goto自然に消えます(注:メインメソッドが呼び出されると仮定しますProcess):

...

private byte[] buffer;
private Queue<TraceItem> queue;

public void Process() {
  queue = new Queue<TraceItem>(batch);
  while (connected) {
    ReceiveMessage();
    TryProcessMessage();
  }
}

private void ReceiveMessage() {
  try {
    socket.Recv(out buffer);
  }
  catch {
    // ignore the exception we get when the socket is shut down from another thread
    // the connected flag will be set to false and we'll break the processing
  }
}

private void TryProcessMessage() {
  try {
    ProcessMessage();
  }
  catch (Exception ex) {
    ProcessError(ex);
  }
}

private void ProcessMessage() {
  if (buffer == null) return;
  var item = TraceItemSerializer.FromBytes(buffer);
  if (item == null) return;
  queue.Enqueue(item);
  if (HasMoreData()) {
    TryProcessMessage();
  }
}

private bool HasMoreData() {
  return queue.Count < batch && socket.Recv(out buffer, ZMQ.NOBLOCK);
}

private void ProcessError(Exception ex) {
  ReceiverPerformanceCounter.IncrementDiagnosticExceptions();
  tracer.TraceException(TraceEventType.Error, 0, ex);
}

...
于 2011-04-18T16:53:50.623 に答える