【问题标题】:Can/Should I replace this GOTO statement in C#我可以/应该在 C# 中替换这个 GOTO 语句吗
【发布时间】:2014-10-09 21:46:37
【问题描述】:

在这里使用 goto 似乎很自然

一个项目需要读取pdf文件,pdf文件可以是以下之一。

  • 不受保护
  • 受密码保护1
  • 受密码保护2
  • 受密码保护3

文件只能使用正确的密码才能访问,无法预先知道文件需要哪个密码。我们必须尝试所有情况,(不包括密码)。如果以上密码均无效,则抛出异常。

PdfReader GetPdfReader(string filePath)
{
    PdfReader r = null;
    int Retries = 0;
    start: try
    {
        switch (Retries)
        {
            case 0: r = new PdfReader(filePath); break;
            case 1: r = new PdfReader(filePath, password1); break;
            case 2: r = new PdfReader(filePath, password2); break;
            case 3: r = new PdfReader(filePath, password3); break;
        }
    }
    catch (BadPasswordException ex)
    {
        if (Retries == 3) throw ex;
        Retries++;
        goto start;
    }
    return r;
}

嵌入 try/catch 可以工作,但看起来很难看,使用 goto 看起来很自然。

两个问题:

  1. 我应该替换这个 goto 吗?
  2. 有没有优雅的方法来替换这个 goto?

谢谢

【问题讨论】:

  • 是的,你应该这样做。为什么你不只使用循环?您正在用 C# 中的 goto... 模拟一个
  • 永远不要使用throw ex 重新抛出异常,而只需使用throwthrow ex 不会像 throw 那样保留原始堆栈跟踪。这对于调试非常有用。
  • 一个简单的while (Retries <= 3) 足以替换这里的gotoswitch() 也不完全是优雅的高度。
  • @EdS.:他不是模拟一个循环,而是实现一个循环。 while 基本上做同样的事情。
  • 是的,更换它。这是没有意义的并且它向上。跳出或跳入嵌套循环可能是件好事,但这里的 goto 没有任何价值。

标签: c# try-catch goto readability


【解决方案1】:

Goto,就像核能或培根一样,并不是天生的邪恶。这取决于你用它做什么。

当前的代码结构相当清晰。 Goto 未被用于创建spaghetti code

话虽如此,我仍然会用while 循环替换它。 Goto 可能是滑坡。

bool passwordIsOK = false;
while (!passwordIsOK && Retries < 3)
{
    // Existing code, without "goto start;".  Set passwordIsOK = true when appropriate.
    // See also @Sriram's comment about using "throw" rather than "throw ex".
}

【讨论】:

  • 我不会引入passwordIsOk,而是在switch 的右大括号之后放置一个break;
  • @GregHewgill:也不错。我个人的风格是对正在发生的事情非常明确,即使这意味着额外的代码行。我觉得当我或其他人多年后维护代码时,它往往会减少错误。你的建议在我的书中很好。对于一个合理的维护者来说,这已经足够清楚了。
【解决方案2】:

我认为你应该在这种情况下替换 goto,因为我的问题是你的代码和重试使意图变得模糊。我个人会用这样的代码替换代码:

PdfReader GetPdfReader(string filePath)
{
    PdfReader r = null;

    string [] passwordsToTry = new string [] {null, password1, password2, password3};

    foreach(string password in passwordsToTry)
    {
        try
        {
            r = (password == null) ? 
                new PdfReader(filePath) 
              : new PdfReader(filePath, password);
            if (r != null) 
               break;
        }
        catch(BadPasswordException){ }
    }
    return r;
}

对我来说,代码更清晰:

  1. 您已定义密码列表以尝试包括“无”
  2. 你不关心 BadPasswordExceptions 只是忽略它们
  3. 如果命中则退出循环
  4. 如果没有命中,循环将在最后退出

另一件事是,如果您必须处理更多或更少的密码,那么您的具有 3 个密码的代码会有点脆弱。而且我认为使用像 passwordsToTry 这样的变量非常适合 'try' 语句。

【讨论】:

  • 修复了 goto 和 switch 问题。太好了!
  • 如果失败则正常退出,但根据问题它应该重新抛出最后一个异常。
【解决方案3】:

你可以用break 来做while(true)

PdfReader GetPdfReader(string filePath)
{
    PdfReader r = null;
    int Retries = 0;
    while(true)
    {
        try
        {
            switch (Retries)
            {
                case 0: r = new PdfReader(filePath); break;
                case 1: r = new PdfReader(filePath, password1); break;
                case 2: r = new PdfReader(filePath, password2); break;
                case 3: r = new PdfReader(filePath, password3); break;
            }
            break;
        }
        catch (BadPasswordException ex)
        {
            if (Retries == 3) throw ex;
            Retries++;
        }
    }
    return r;
}

这避免了添加任何额外的变量。 This topic 总结了 goto 的优缺点。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-11-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-12-30
    • 2011-11-16
    • 2011-02-11
    相关资源
    最近更新 更多