【问题标题】:What is the perfect way to write "return" in a method在方法中写“return”的完美方式是什么
【发布时间】:2012-01-15 21:56:30
【问题描述】:

我不喜欢方法有多个返回行。所以我用字符串结果创建了一个返回值 - 在每种情况下我都会写 result = something...

但是当我写“try-catch”机制时,我必须设置公共字符串结果。因为,如果我在 try 中返回结果,编译器将启动错误,并说并非所有代码都有返回值。如果我将 result = string.Empty 写到方法的末尾,resharper 说,这不是可访问的代码。所以,这里是一个例子,这是我的问题;

“在方法中写“return”的完美方式是什么?”

    public static string PingThatAddress(string hostAddress)
    {
        try
        {
            Ping ping = new Ping();
            PingReply pingreply = ping.Send(hostAddress);

            string result;
            if (pingreply != null && pingreply.Status.ToString() != "TimedOut")
            {
                result = "Address: " + pingreply.Address + "\r"
                     + "Roundtrip Time: " + pingreply.RoundtripTime + "\r"
                     + "TTL (Time To Live): " + pingreply.Options.Ttl + "\r"
                     + "Buffer Size: " + pingreply.Buffer.Length + "\r";
            }
            else
            {
                result = string.Empty;
            }

            return result;
        }
        catch (Exception pingError)
        {
            Debug.Fail(pingError.Message + " " + pingError);
        }
        //compiler error: THERE IS NO RETURN VALUE here?
    }

【问题讨论】:

  • “我不喜欢方法有几个返回行” - 为什么?这是简化方法的最佳方式之一。
  • 您期待哪些异常,为什么还要捕获并处理其他异常?
  • 如果您对使用多个返回语句有疑问,请查看stackoverflow votes on this matter
  • @Oded 我也不喜欢方法中的多个返回。退货只不过是隐藏在漂亮布中的“goto”。

标签: c# coding-style resharper


【解决方案1】:

你可以这样做:

public static string PingThatAddress(string hostAddress)
{
    string result = string.Empty;
    try
    {
        Ping ping = new Ping();
        PingReply pingreply = ping.Send(hostAddress);

        if (pingreply != null && pingreply.Status.ToString() != "TimedOut")
        {
            result = "Address: " + pingreply.Address + "\r"
                 + "Roundtrip Time: " + pingreply.RoundtripTime + "\r"
                 + "TTL (Time To Live): " + pingreply.Options.Ttl + "\r"
                 + "Buffer Size: " + pingreply.Buffer.Length + "\r";
        }

    }
    catch (Exception pingError)
    {
        Debug.Fail(pingError.Message + " " + pingError);
    }
    return result;
}

然后只需确保将result 设置为在异常情况下有意义的设置。

如果您试图坚持 Resharper 警告您的内容,请这样做:

public static string PingThatAddress(string hostAddress)
{
    try
    {
        Ping ping = new Ping();
        PingReply pingreply = ping.Send(hostAddress);

        string result = string.Empty;
        if (pingreply != null && pingreply.Status.ToString() != "TimedOut")
        {
            result = "Address: " + pingreply.Address + "\r"
                 + "Roundtrip Time: " + pingreply.RoundtripTime + "\r"
                 + "TTL (Time To Live): " + pingreply.Options.Ttl + "\r"
                 + "Buffer Size: " + pingreply.Buffer.Length + "\r";
        }
        return result;

    }
    catch (Exception pingError)
    {
        Debug.Fail(pingError.Message + " " + pingError);
    }
    return string.Empty;
}

你不能在这里两全其美:你的人为标准没有多个 return 语句可能是导致你在使用 Resharper 时遇到问题的原因。

【讨论】:

  • 非常感谢您的回答。但现在,resharper 说; “将 decleration 移至更紧密的使用”。我问这个问题,什么是在编码标准中做到这一点的完美方法。在任何情况下,我都不想忽略 resharper 推荐。
  • 如果我将 result = string.Empty 写到方法的末尾,resharper 说,它是不可访问的代码。该死的resharper =)但我认为有最好的方法来做到这一点。我不知道。它必须是最好的方法......
  • @LostInLib 我没说result = string.Empty;我说return string.Empty。看看我的新答案
  • 我以第一种方式作为答案。我认为这是做到这一点的完美方式。非常感谢安德鲁...
【解决方案2】:

一些建议,包括已接受答案的第一部分和原始问题,很可能会返回不正确的结果,尤其是在出现异常时。

问题是,我们已经多次看到这种情况发生了,当您只有一个返回值时,对方法逻辑的微小更改总是会导致原始方法编写者没有预料到的代码路径,并且将导致在方法中多次错误设置返回变量或根本不设置。

有时您必须收集一个值以返回给调用者,然后在随后的方法中执行一些额外的任务,但总的来说,这应该是例外而不是规则。

在追踪了太多由于希望拥有单一返回值而引入的错误之后,我们的开发标准现在规定除非绝对必要,否则将使用return,并且必须在代码以及后续修饰符的警告。

这种方法的额外好处是,如果方法的逻辑被修改为新的代码路径导致返回逻辑中的“漏洞”,编译器将自动通知您。使用单个返回值要求开发人员目视检查每个可能的代码路径,以验证没有遗漏任何内容。

最后,我们要求从异常处理程序中返回适当的默认值,而不是在异常之外有返回值。这样一来,发生异常时会发生什么就一目了然了。

所以在我们的环境中,您的代码将是:

public static string PingThatAddress(string hostAddress)
{
    try
    {
        Ping ping = new Ping();
        PingReply pingreply = ping.Send(hostAddress);

        if (pingreply != null && pingreply.Status.ToString() != "TimedOut")
        {
            return "Address: " + pingreply.Address + "\r"
                 + "Roundtrip Time: " + pingreply.RoundtripTime + "\r"
                 + "TTL (Time To Live): " + pingreply.Options.Ttl + "\r"
                 + "Buffer Size: " + pingreply.Buffer.Length + "\r";
        }
        else
        {
            return string.Empty;
        }
    }
    catch (Exception pingError)
    {
        Debug.Fail(pingError.Message + " " + pingError);
        return string.Empty;
    }
}

【讨论】:

  • 非常感谢您的回答。你是对的,但我会尝试做 resharper 向我推荐的所有事情(并非总是如此,但我会尝试)。 resharper 对你的代码说; “else”块和“return string.Empty;”在 catch 块中是无法访问的代码。 - 我点击你的答案有用。
  • 这是一个很好的通用方法(遵循 resharper 的说明),但在这种情况下,resharper 中似乎存在错误。首先,在发布模式下,Debug.Fail 永远不会被执行,因此会返回。其次,在调试模式下,Debug.Fail 所做的只是显示一条消息,让用户决定要做什么。至少提供的选择之一是忽略异常并继续,在这种情况下也将达到返回。因此,此代码的 resharper 分析存在错误。
  • 在“Andrew Barber”的第一个答案中,Debug.Fail 永远不会在已编译的应用程序中执行(不是调试,在发布模式下编译)。但是程序将继续退出 catch 块并读取以下语句“return result;”(结果为 string.Empty )。我对吗?那么在哪种情况下呢? & 会出现什么错误?
  • 在他的回答中设计的方法中没有问题。但是,假设有人在设置结果后引入了一些新代码导致异常,或者更糟糕的是,有人在设置结果的代码区域添加了另一行以向结果添加更多内容,并且该代码导致异常。在这两种情况下,result 都将包含一个错误地返回给调用者的值。
【解决方案3】:

所以您声称帮助您到达该出口点的if 语句(或其他程序流程语句)实际上本身并不是一个出口点?该控制语句和return 语句之间的唯一区别是return 语句实际上更可见、更易读、更易于维护且不易出错。此外,由于 return 语句适用于任何级别,您可能会重复控制语句 x 次。

希望在最后使用 return 语句的唯一原因是资源处理 - 摆脱您在开始时声明的资源。这在 C/C++ 中更为常见(即使在 C++ 中,它也变得不那么麻烦了)。在 Java 中,您可以依靠异常机制和 finally 语句来处理应有的资源(在 Java 7 中,“尝试使用资源”功能)。

选择return 语句还有其他原因。一个是将变量标记为 final 的能力:因为编译器知道 return (或就此而言的抛出)是方法的结尾,您可以更轻松地插入 final 变量。最终变量——一旦你习惯了它们——真的可以让代码更容易阅读并帮助你避免犯错。

  boolean someErrorCondition = true;
  final int setSomething;
  for (int i = 0; i < 8; i++) {
      if (i == 7) {
          setSomething = 11;
          break;
      }

      if (someErrorCondition) {
          return;
      }
  }

这不适用于您的方法,因为编译器会抱怨未设置最终变量(这是整个想法,没有进入无效状态)。

许多优秀的开发人员和语言设计人员都选择了支持多个返回语句。我会紧急建议任何人不要朝另一个方向前进。

【讨论】:

  • 抱歉,示例中使用的是 Java,但您明白了
【解决方案4】:

如果发生异常,您不会返回值。

【讨论】:

  • OP 正在捕获并处理异常; 可能可能不在这里返回值是合适的。
  • 非常感谢您的回答。但我不明白。为什么我不应该在 try-catch 块中返回值?
  • 在我看来,LostInLib 可以在 catch 块中编写返回语句或使用您的代码。虽然您的代码的增强是一个单一的返回语句。
  • @rekire 我的代码是为了防止编译错误,它确实主要是添加一个 return 语句......但你的“在我看来”是完全一样的:添加一个 return陈述。不过,我对代码的改进是初始化 result 变量,然后删除不需要的 else 块。
  • @AndrewBarber 开发人员应该尝试并且在其代码正常运行时不要在其代码中抛出任何异常。捕获异常并返回的唯一原因是,如果外部 API 返回了一个您无法合理地将其放入另一个异常的异常——这对您的应用程序来说是一个“好”的场景。幸运的是,这正是 OP 的代码 LostInLib 中发生的事情。
【解决方案5】:

多个返回语句使您更容易遵循您的代码。通过这种方式,您会立即看到不同的方法返回点。相反,一个返回的“结果”字段会迫使您找到该字段在更改后的使用位置。它使遵循该方法的自然流程变得更加困难。但无论如何,通常决定更多的是关于风格偏好。确保不要混合使用两种方法。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-03-12
    • 1970-01-01
    • 1970-01-01
    • 2013-01-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-05-17
    相关资源
    最近更新 更多