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