这个 goto 有表达力吗?

发布于 2024-09-08 05:46:30 字数 1182 浏览 12 评论 0原文

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

The following code was a proof of concept for a message batching routine. Do I avoid goto like the plague and rewrite this code? Or do you think the goto is an expressive way to get this done?

If you'd rewrite please post some code...

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
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(10

怀念你的温柔 2024-09-15 05:46:30

Goto 将带您进入一些棘手的情况

几乎总结了我对“goto”的想法。

由于多种原因,Goto 是一种糟糕的编程习惯。其中最主要的是几乎从来没有理由。有人发布了一个 do..while 循环,请使用它。使用布尔值来检查是否应该继续。使用 while 循环。 Goto 是针对解释性语言的,并且是对汇编程序时代的回顾(JMP 有人吗?)。您使用高级语言是有原因的。这样您和其他人就不会在查看代码时迷失方向。


为了使这个答案保持最新状态,我想指出 goto 和支撑错误的组合导致了 iOS 和 OS X 中的主要 SSL 错误

Goto will get you into some sticky situations

Pretty much sums up my thoughts on "goto."

Goto is bad programming practice for many reasons. Chief among them is that there is almost never a reason for it. Someone posted a do..while loop, use that. Use a boolean to check if you should continue. Use a while loop. Goto's are for interpreted languages and a call back to assembler days (JMP anyone?). You're using a high level language for a reason. So that you and everyone else doesn't look at your code and get lost.


To keep this answer somewhat current I'd like to point out that a combination of goto and bracing errors caused a major SSL bug in iOS and OS X.

御弟哥哥 2024-09-15 05:46:30

如果您不想要现在拥有的“总是运行一次”功能,请将 goto 替换为 do-while 或简单的 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
}

Replace the goto with a do-while, or simply a while loop if you don't want the "always run once" functionality you have right now.

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
}
一张白纸 2024-09-15 05:46:30

在这种情况下,摆脱 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
}

It's so amazingly easy to rid yourself of GOTO in this situation it makes me cry:

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
}
两人的回忆 2024-09-15 05:46:30

我想 goto 直观上更具可读性......但如果你想避免它,我认为你所要做的就是将代码放入 while(true) 循环中,然后循环末尾的 break 语句用于正常迭代。并且 goto 可以替换为 continue 语句。

最终,您只需学习读写循环和其他控制流结构,而不是使用 goto 语句,至少根据我的经验。

I guess the goto is SLIGHTLY more readable intuitively... But if you WANTED to avoid it I think all you'd have to do is throw the code in a while(true) loop, and then have a break statement at the end of the loop for a normal iteration. And the goto could be replaced with a continue statement.

Eventually you just learn to read and write loops and other control flow structures instead of using goto statements, at least in my experience.

面如桃花 2024-09-15 05:46:30

有点与 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 维数组映射到一维并仅使用一维虽然但我相信嵌套的可读性要强得多。

顺便说一句,我并不是说我们都应该使用 goto,但在这种特定情况下我会按照我刚才提到的方式来做。

Kind of related to Josh K post but I'm writing it here since comments doesn't allow code.

I can think of a good reason: While traversing some n-dimensional construct to find something. Example for 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...

I know you can use "n" whiles and booleans to see if you should continue.. or you can create a function that maps that n-dimensional array to just one dimension and just use one while but i believe that the nested for its far more readable.

By the way I'm not saying we should all use gotos but in this specific situation i would do it the way i just mentioned.

七婞 2024-09-15 05:46:30

你可以重构为这样的东西。

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);
    }
}

You could refactor is to something like this.

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);
    }
}
箜明 2024-09-15 05:46:30

嗯,我不太确定你想跳出 try 块。我很确定这不是一件安全的事情,尽管我对此不是 100% 确定。这看起来不太安全......

Umm, I'm not really sure you want to goto out of a try block. I'm pretty sure that is not a safe thing to do, though I'm not 100% sure on that. That just doesn't look very safe...

那小子欠揍 2024-09-15 05:46:30

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

Wrap the "HaveAnotherMessage" into a method that takes in the buffer and may call itself recursively. That would seem to be the easiest way to fix this.

一抹淡然 2024-09-15 05:46:30

在这种情况下我会避免 goto 并重构它。我认为该方法读起来太长了。

I would avoid goto in this case, and refactor it. The method reads too long in my opinion.

天涯离梦残月幽梦 2024-09-15 05:46:30

我觉得你的方法太大了。它混合了不同级别的抽象,例如错误处理、消息检索和消息处理。

如果你用不同的方法重构它,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);
}

...

I think your method is too big. It mixes different levels of abstraction, like error processing, message retrieval and message processing.

If you refactor it in different methods, the goto naturally goes away (note: I assume your main method is called 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);
}

...
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文