这个转到表达吗?

时间:2010-07-07 18:36:18

标签: c# goto

以下代码是消息批处理例程的概念证明。我是否像瘟疫一样避免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
}

10 个答案:

答案 0 :(得分:44)

Goto will get you into some sticky situations

几乎总结了我对“转到”的看法。

由于许多原因,Goto是糟糕的编程习惯。其中最主要的是几乎没有理由。有人发布了do..while循环,使用它。使用boolean检查是否应该继续。使用while循环。 Goto用于解释语言和回调汇编日(JMP任何人?)。您出于某种原因使用高级语言 。这样你和其他人就不会看你的代码而迷路了。


为了使这个答案保持最新,我想指出goto和支撑错误的组合导致了major SSL bug in iOS and OS X

答案 1 :(得分:19)

如果您不想使用现在的“始终运行一次”功能,请使用do-while或简单的while循环替换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
    }

    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
}

答案 2 :(得分: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
}

答案 3 :(得分:5)

我认为goto在直觉上更具可读性......但如果你想避免它,我认为你所要做的就是将代码放在while(true)循环中,然后有一个{{ 1}}语句在循环结束时进行正常迭代。 break可以用goto语句替换。

最后,您只是学会读取和编写循环和其他控制流结构,而不是使用continue语句,至少在我的经验中。

答案 4 :(得分: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”whiles和booleans看看你是否应该继续..或者你可以创建一个函数,将n维数组映射到一个维度,只使用一个但我相信嵌套因为它更具可读性。

顺便说一下,我并不是说我们都应该使用gotos,但在这种特殊情况下,我会按照刚刚提到的方式进行。

答案 5 :(得分: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);
    }
}

答案 6 :(得分:0)

嗯,我真的不确定你想要试一试。我很确定这不是一件安全的事情,尽管我并不是100%肯定。这看起来不太安全......

答案 7 :(得分:0)

将“HaveAnotherMessage”包装到一个接收缓冲区的方法中,并可以递归调用自身。这似乎是解决这个问题的最简单方法。

答案 8 :(得分:0)

在这种情况下我会避免转到,并重构它。在我看来,这种方法读得太久了。

答案 9 :(得分: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);
}

...