以下代码是消息批处理例程的概念证明。我是否像瘟疫一样避免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
}
答案 0 :(得分:44)
由于许多原因,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)
答案 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);
}
...