使用goto的最佳实践

6

在这段代码中使用goto是正确的吗?是否有其他替代方法?

return ExecuteReader(cmd, reader =>
{
    List<BEPartnership> partnerhip = null;

    //Partnership
    if (!((SqlDataReader) reader).HasRows)
        goto exit;

    partnerhip = 
        new List<BEPartnership>{new BEPartnership().GetFromReader(reader)};

    //Customers
    if (!reader.NextResult() && !((SqlDataReader) reader).HasRows)
        goto exit;

    foreach (BEPartnership p in partnerhip)
        p.Partner = new BECustomer().GetFromReader(reader);

    //Contracts
    if (!reader.NextResult() && !((SqlDataReader) reader).HasRows)
        goto exit;

    List<BEContractB2B> contracts = new List<BEContractB2B>();
    contracts.Add(new BEContractB2B().GetFromReader(reader));
    // contracts = new BEContractB2B().GetFromReader2(reader).ToList();

    exit:
    return partnerhip;
});

18
最佳实践是:不要使用GOTO! - Steven
4
@Steven:那样表述过于简单化。 - H H
3
在某些情况下,使用goto非常有用。 - Alexandre
3
有些情况下,使用goto是非常无用的。 - Flinsch
6
每次你编写循环时,编译器都会在编译的中间语言中放置一个goto。中间语言只有三种基本控制流:抛出异常、跳转和返回。几乎所有东西都是跳转。结构化编程的目的是选择比goto更具意义的控制流,而不是完全避免分支。 - Eric Lippert
显示剩余9条评论
5个回答

19
你可以将每个goto exit;替换为return null;return partnership;,如果你想返回当前填充的列表。(我假设一个“partnerhip”是一个很酷的伙伴?)

8
我觉得不行。
自2001年以来,我一直在使用C#编程,从未使用过goto!
如果你想在代码中实现“短路退出”,为什么不替换掉 "
goto exit:

使用

return partnership

2
写短小的函数,你就不需要使用 goto。Amen. - Steven

5

goto关键字和“最佳实践”是互相排斥的,在我的看法(很可能也是大多数人的看法)中,需要使用goto语句表明代码设计存在问题。在你的情况下,解决方案似乎很简单:我认为你只需要将goto exit替换为return partnership,并删除标签exit:。(是否应该将“partnerhip”更正为“partnership”?)


1

你在最后做的是从读取器中加载合同(如果有的话)。如果你用一个简单的if语句明确表达这个意图,它会读起来更好。

将结尾改为:

if (reader.NextResult() || ((SqlDataReader) reader).HasRows)
{
    List<BEContractB2B> contracts = new List<BEContractB2B>();
    contracts.Add(new BEContractB2B().GetFromReader(reader));
}

return partnerhip;

虽然看起来你只是忽略了合同列表...但它并没有做任何事情。除非创建一个新的BEContractB2B类具有一些全局副作用(坏消息),否则你可以完全摆脱它。

将第一个goto更改为

if (!((SqlDataReader) reader).HasRows)
    return null;

既然你正在这样做,那么你应该明确表明你将返回 null。


1
我发现在编程中goto语句有时可能会有一些用处:When To Use Goto When Programming in C。但是“永远不要使用GOTO”是我在大学里学到的第一件事,所以我从来没有使用过它(至少不是在C、C++、C#、Java等语言中)。
GOTO语句最大的问题在于,如果你阅读一个方法的代码片段,你无法看出它可能被哪里调用。例如:
int a = 1;
division:
int b = 4 / a;

...听起来还不错。但是如果在除法块之后有以下GOTO,则会导致0除错误崩溃:

int a = 1;
division:
int b = 4 / a;
// ... hundreds of lines ...
a = 0;
goto division;

如果在除法块之前有GOTO,则返回null异常崩溃:

goto division;
// ... hundreds of lines ...
int a = 1;
division:
int b = 4 / a;

...这只是一个例子,GOTO会导致更多争议的情况。所以请忘记GOTO,这样人们(包括你)在阅读代码时会更加愉快。

使用"return partnership;"代替你的goto。


实际上,你的第二个例子甚至无法编译。首先,a 是一个值类型,永远不可能为 null。其次,编译器将检测到 int a = 1; 是不可达的,并首先发出不可达代码警告,然后是未分配本地变量错误,这将导致代码无法编译。 - Harry Steinhilber

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接