Является ли это 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
}

Ответ 1

Замените 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
}

Ответ 2

Goto will get you into some sticky situations

В значительной степени подводит итог моих мыслей по поводу "goto".

Goto - плохая практика программирования по многим причинам. Главным из них является то, что для этого почти никогда нет причин. Кто-то опубликовал цикл do..while while, используйте это. Используйте boolean чтобы проверить, следует ли продолжать. Используйте цикл while. Goto для интерпретируемых языков и обратного вызова в дни ассемблера (JMP кто-нибудь?). Вы используете язык высокого уровня по причине. Чтобы вы и все остальные не смотрели на ваш код и не терялись.


Чтобы этот ответ был несколько актуальным, я хотел бы отметить, что сочетание ошибок goto и bracing привело к серьезной ошибке SSL в iOS и OS X.

Ответ 3

Это так удивительно легко избавиться от 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
}

Ответ 4

Я думаю, что goto СЛАБАЯ более понятна интуитивно... Но если вы ХОТЯТ избежать этого, я думаю, что все, что вам нужно сделать, это выбросить код в цикле while(true), а затем иметь оператор break в конце цикла для нормальной итерации. И goto можно заменить выражением continue.

В конце концов вы просто научитесь читать и писать циклы и другие структуры потока управления вместо использования инструкций goto, по крайней мере, в моем опыте.

Ответ 5

Относительно сообщения 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 get out
            }
ENDFOR: ;
//MORE CODE HERE...

Я знаю, что вы можете использовать "n" whiles и boolean, чтобы увидеть, следует ли продолжить. Или вы можете создать функцию, которая отображает этот n-мерный массив только на одно измерение и просто использовать его, но я считаю, что вложенный потому что он гораздо читабельнее.

Кстати, я не говорю, что мы все должны использовать gotos, но в этой конкретной ситуации я бы сделал это так, как я только что упомянул.

Ответ 6

Вы могли бы реорганизовать что-то вроде этого.

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

Ответ 7

Умм, я не уверен, что ты хочешь выйти из блока try. Я уверен, что это небезопасно, хотя я не уверен на этом. Это просто не выглядит очень безопасным...

Ответ 8

Оберните "HaveAnotherMessage" в метод, который берет в буфере и может называть себя рекурсивно. Это, казалось бы, самый простой способ исправить это.

Ответ 9

В этом случае я бы избежал goto и реорганизовал его. Метод, по моему мнению, слишком длинный.

Ответ 10

Я думаю, что ваш метод слишком велик. Он смешивает различные уровни абстракции, такие как обработка ошибок, поиск сообщений и обработка сообщений.

Если вы реорганизуете его разными способами, 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);
}

...