C#/GOTO 就在这里吗?
天哪,C# 中对 GOTO 语句的热情;我什至害怕问这个问题。
类似的问题还有很多;这也让我有点紧张。但我是认真的。
请抵制那些简单地否定 GOTO 语句的回应。
然而,我有点困惑为什么这个实现对于 GOTO 来说并不理想:
public event CancelEventHandler DeleteSnapshotStarted;
public event AsyncCompletedEventHandler DeleteSnapshotCompleted;
public void DeleteSnapshot(Guid documentId, Action<Exception> callback)
{
if (!this.Snapshots.Where(x => x.DocumentId == documentId).Any())
throw new Exception("Snapshot not found; ensure LoadSnapshots()");
// define action
var _Action = new Action(() =>
{
// preview
bool _Cancelled = false;
if (DeleteSnapshotStarted != null)
{
CancelEventArgs _CancelArgs = new CancelEventArgs { };
DeleteSnapshotStarted(this, _CancelArgs);
if (_CancelArgs.Cancel)
{
_Cancelled = true;
goto END;
}
}
// execute
Exception _Error = null;
try
{
Proxy.CoreService.DeleteSnapshot(documentId);
LoadSnapshots(null);
}
catch (Exception ex) { _Error = ex; }
END:
// complete
if (DeleteSnapshotCompleted != null)
{
AsyncCompletedEventArgs _CompleteArgs =
new AsyncCompletedEventArgs(_Error, _Cancelled, null);
DeleteSnapshotCompleted(this, _CompleteArgs);
}
// bubble error
if (_Error != null)
throw _Error;
});
// run it
if (callback == null) { _Action(); }
else
{
using (BackgroundWorker _Worker = new BackgroundWorker())
{
_Worker.DoWork += (s, arg) => { _Action(); };
_Worker.RunWorkerCompleted += (s, arg) => { callback(arg.Error); };
_Worker.RunWorkerAsync();
}
}
}
** 我给出 - 我会避免 GOTO! :D**
这似乎是最好的:
public event CancelEventHandler DeleteSnapshotStarted;
public event AsyncCompletedEventHandler DeleteSnapshotCompleted;
public void DeleteSnapshot(Guid documentId, Action<Exception> callback)
{
if (!this.Snapshots.Where(x => x.DocumentId == documentId).Any())
throw new Exception("Snapshot not found; ensure LoadSnapshots()");
// define action
var _Action = new Action(() =>
{
// preview
CancelEventArgs _CancelArgs = new CancelEventArgs { };
if (DeleteSnapshotStarted != null)
DeleteSnapshotStarted(this, _CancelArgs);
// execute
Exception _Error = null;
if (!_CancelArgs.Cancel) try
{
Proxy.CoreService.DeleteSnapshot(documentId);
LoadSnapshots(null);
}
catch (Exception ex) { _Error = ex; }
// complete
if (DeleteSnapshotCompleted != null)
DeleteSnapshotCompleted(this,
new AsyncCompletedEventArgs(null, _CancelArgs.Cancel, null));
// bubble
if (_Error != null)
throw _Error;
});
// run it
if (callback != null)
{
using (BackgroundWorker _Worker = new BackgroundWorker())
{
_Worker.DoWork += (s, arg) => { _Action(); };
_Worker.RunWorkerCompleted += (s, arg) =>
{ callback(arg.Error); };
_Worker.RunWorkerAsync();
}
}
else
_Action();
}
谢谢大家。
Oh boy, the passion around GOTO statements in C#; I dread even asking this question.
So many questions similar to this; that also makes me a bit nervous. But I am serious.
Please resist the responses that simply dismiss the GOTO statement wholesale.
However, I am a little stumped to see why this implementation is not ideal for GOTO:
public event CancelEventHandler DeleteSnapshotStarted;
public event AsyncCompletedEventHandler DeleteSnapshotCompleted;
public void DeleteSnapshot(Guid documentId, Action<Exception> callback)
{
if (!this.Snapshots.Where(x => x.DocumentId == documentId).Any())
throw new Exception("Snapshot not found; ensure LoadSnapshots()");
// define action
var _Action = new Action(() =>
{
// preview
bool _Cancelled = false;
if (DeleteSnapshotStarted != null)
{
CancelEventArgs _CancelArgs = new CancelEventArgs { };
DeleteSnapshotStarted(this, _CancelArgs);
if (_CancelArgs.Cancel)
{
_Cancelled = true;
goto END;
}
}
// execute
Exception _Error = null;
try
{
Proxy.CoreService.DeleteSnapshot(documentId);
LoadSnapshots(null);
}
catch (Exception ex) { _Error = ex; }
END:
// complete
if (DeleteSnapshotCompleted != null)
{
AsyncCompletedEventArgs _CompleteArgs =
new AsyncCompletedEventArgs(_Error, _Cancelled, null);
DeleteSnapshotCompleted(this, _CompleteArgs);
}
// bubble error
if (_Error != null)
throw _Error;
});
// run it
if (callback == null) { _Action(); }
else
{
using (BackgroundWorker _Worker = new BackgroundWorker())
{
_Worker.DoWork += (s, arg) => { _Action(); };
_Worker.RunWorkerCompleted += (s, arg) => { callback(arg.Error); };
_Worker.RunWorkerAsync();
}
}
}
** I give - I'll avoid GOTO! :D**
Here's what seems best:
public event CancelEventHandler DeleteSnapshotStarted;
public event AsyncCompletedEventHandler DeleteSnapshotCompleted;
public void DeleteSnapshot(Guid documentId, Action<Exception> callback)
{
if (!this.Snapshots.Where(x => x.DocumentId == documentId).Any())
throw new Exception("Snapshot not found; ensure LoadSnapshots()");
// define action
var _Action = new Action(() =>
{
// preview
CancelEventArgs _CancelArgs = new CancelEventArgs { };
if (DeleteSnapshotStarted != null)
DeleteSnapshotStarted(this, _CancelArgs);
// execute
Exception _Error = null;
if (!_CancelArgs.Cancel) try
{
Proxy.CoreService.DeleteSnapshot(documentId);
LoadSnapshots(null);
}
catch (Exception ex) { _Error = ex; }
// complete
if (DeleteSnapshotCompleted != null)
DeleteSnapshotCompleted(this,
new AsyncCompletedEventArgs(null, _CancelArgs.Cancel, null));
// bubble
if (_Error != null)
throw _Error;
});
// run it
if (callback != null)
{
using (BackgroundWorker _Worker = new BackgroundWorker())
{
_Worker.DoWork += (s, arg) => { _Action(); };
_Worker.RunWorkerCompleted += (s, arg) =>
{ callback(arg.Error); };
_Worker.RunWorkerAsync();
}
}
else
_Action();
}
Thanks everyone.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(11)
是的,您甚至已经有了标志变量:
Yes, you even already have the flag variable:
更改
为:
和 END: 更改为:
Change
to this:
and END: to this:
原则上,值得避免代码中的非本地分支,因为可读性。在您的情况下,可以使用标志变量来重构控制流。有关详细信息,请参阅@NeilN 和@minitech 答案。
在实践中,有时(在极少数情况下:)使用
goto
来解决复杂的控制流程是很有用的,其中正常的if/else/break/while/for
结构会比必要的更加嵌套或复杂。goto 的最佳“好”用法(我现在能想到的)是打破一组深度嵌套的循环,而无需在每次循环迭代上进行额外的条件检查。在单层嵌套中,您可以使用
break
- 但对于许多嵌套层,它会变得更加痛苦。这是一个例子:In principle, it's worth avoiding non-local branching in code, for readability. In your case, it's possible to restructure the flow of control with a flag variable. See @NeilN and @minitech answers for the details.
In practice, it is sometimes (in rare cases :) useful to use
goto
to resolve complex flow of control scenarios where normalif/else/break/while/for
structures would be more nested or convoluted than necessary.The best "good" use of a goto (that I can think of right now) is to break out of a deeply nested set of loops without the overhead of additional conditional checks on each loop iteration. From a single level of nesting you could use
break
- but with many nested levels it becomes more painful. Here's an example:从表面上看,您可以用
if (!_Cancelled) { ... }
包装try/catch
。目前,按照您的方式(从您提供的代码中),您没有在任何地方使用_Cancelled
。新代码如下所示:From the looks of it, you can wrap the
try/catch
withif (!_Cancelled) { ... }
. Currently the way you have it (from the code you've made available), you're not using_Cancelled
anywhere. The new code would look like:为什么不在 GOTO 和标签之间的那些行周围加上
if (!_Cancelled)
呢?Why not just an
if (!_Cancelled)
around those lines between the GOTO and the label?一般情况:
重构代码从而不需要 goto 会更清晰、更易于维护。这是一个很大的方法,应该稍微分解一下。
有时 goto 是一个不错的选择,但很多时候,当简单的重构就足够了时,往往会使用它。
就您而言:
在您的情况下,从许多其他答案看来,建议使用取消的标志可以解决您的问题,而无需转到。
In general:
It would be much clearer and more maintainable to instead refactor the code so that the goto is not necessary. It's a big method as it is and should be broken down a bit.
Occasionally goto is a good choice, but a lot of the time it tends to be used when a simple refactoring would suffice.
In Your Case:
In your case it looks like from a lot of the other answers suggesting using the cancelled flags would solve your problem without the goto.
尝试用此包装快照的删除,并删除
GOTO
和END
标签Try wrapping the deletion of the snapshot with this and remove the
GOTO
and theEND
label只需在 if 语句中使用已取消的变量即可查看是否应该跳过其余代码。
Just use your canceled variable in an if statement to see if you should skip the rest of the code.
您的代码中不需要 goto。这只会让事情变得更加复杂。这是没有它的等效版本。
There's no need for goto in your code. It only makes it more complicated. Here's an equivalent version without it.
因为开关……中断可以更干净地完成工作。
您首先将 CancleArgs 声明为枚举 { ... Canceled, ..., END}
无需重构,因为它一开始就应该这样编写。 ;)
Because a switch ... break could have done the job cleaner.
You first declare CancleArgs as an enum { ... Canceled, ..., END}
No refactoring because it should have been written this way to begin with. ;)