C#方法名中什么时候应该使用“Try”?

209

我们正在与同事讨论方法名称以 "Try" 开头的含义。

有以下两种不同的观点:

  • 当方法可能返回 null 值时,请使用 "Try"。
  • 当方法不会抛出异常时,请使用 "Try"。

官方定义是什么?在方法名称中,"Try" 表示什么意思?是否有一些官方指南来规范这个问题?


91
那些在函数命名上花费很多心思的人实际上是在为“下一个人”着想。不确定为什么会有人靠近投票(这话是来自一个今晚投了很多票的人)。 - Jonathon Reinhart
7
@JonathonReinhart,这个问题被投了关闭票,因为“目前的情况下,这个问题不适合我们的问答格式。我们期望回答能够基于事实、参考资料或专业知识,但这个问题很可能引发辩论、争论、民意调查或长时间的讨论。” - Pranav Hosangadi
19
微软有一份官方声明回答了这个问题(请见我的回答)。这难道不是事实吗? - Erik Schierboom
7
正如Erik所提到的,这是有事实支持的。此外,这里还有许多非常有经验的C#开发人员,他们拥有特定的专业知识来提供一个有效的答案。而且,Eric Lippert是C#语言首席架构师。我认为你可以称之为特定的专业知识 - Jonathon Reinhart
4
“@ErikSchierboom说这是MS的指南是事实。MS指南是否正确可用是主观和有争议的。” - Servy
显示剩余6条评论
7个回答

172

这被称为TryParse模式,由微软公司进行了记录。 官方的Exceptions and Performance MSDN页面说

考虑在常见情况下可能引发异常的成员中使用TryParse模式,以避免与异常相关的性能问题。

因此,如果您有一段代码,在常规使用情况下可能会引发异常(例如解析int),那么TryParse模式是有意义的。


2
另一个有用的链接,记录了这个模式(搜索TryParse)http://blogs.msdn.com/b/kcwalina/archive/2005/03/16/396787.aspx - V Maharajh
2
基本上,如果你有一个 TryParse 方法,你应该有一个 Parse 方法,当 TryParse 返回 false 时抛出异常。相反地,如果你有一个 Parse 方法,你应该考虑有一个 TryParse 方法,当 Parse 抛出异常时返回 false。 - 3Doubloons
5
补充一下,通常情况下,异常处理是针对“特殊”情况的。如果你正在进行的操作很容易失败,并且这种失败并不特别引人注目,那么使用这种模式比使用try/catch更符合惯用法。 - Adam Robinson
21
这是基础知识,但这并不意味着指南没有用。如果你不够了解平台,那么做到基础知识也可能很难。因此,遵循指南仍然十分重要。 - Erik Schierboom
1
@daveL:这很基础,但是不同的约定(ParseOrNullParse)或者甚至相反的约定(ParseParseOrThrow)也同样基础。微软指南的好处在于它促进了一致性:当你看到一个你不熟悉的代码库时,你仍然可以理解它,因为它(很可能)使用了平台所有者认可的广泛约定。 - ruakh
显示剩余3条评论

135

(已更正) 有官方指南,正如Erik所建议的那样。

当我看到 TrySomething 方法时,我会假设它:

  • 不会抛出异常
  • 返回 bool
  • 如果我期望值,它将通过“out”参数返回
  • 存在 Something 方法,使我能够自己处理任何异常。(编辑,由Jesse Webb建议)

4
更正 - 它有官方指南。请参考Erik的回答。 - nothrow
10
我还有第四个期望:如果有一个名为TryFoo的方法,那么会有一个类似的Foo方法,允许我自己处理任何异常。这些方法的签名可能不同,因此在没有其他代码更改的情况下,它们的用法是不能互换的。 - Jesse Webb
1
@JesseWebb,感谢您指出这一点。如果您不介意的话,我已经将您的评论添加到我的答案中了。 - nothrow
1
“不会抛出异常” 对我而言似乎是过于笼统的说法。例如,Int32.TryParse(String, NumberStyles, IFormatProvider, Int32) 如果不喜欢样式参数,则会抛出 ArgumentException 异常。 - Jirka Hanika
1
我同意“不抛出异常”可能被认为是过于概括,但我相信意图是表达它不会因参数的值而抛出异常,而是因执行结果而不抛出异常。 - ConfusingBoat

10

我认为在想要继续执行时应该使用try。无论方法是否返回某个值都没有关系。

情况1:如果它有返回值,你可以以某种方式继续进行。

情况2:如果它没有返回值,仍然可以继续以其他方式进行。

如果你期望某个方法返回一些值,则使用out参数。

示例

int value
if (dictionary.TryGetValue("key", out value))
{
    // Proceed in some way
}
else
{
    // Proceed in some other way
}

6

如果您的方法不会抛出任何异常,或者您的方法具有以下签名:bool TrySomething(input, out yourReturn),请确保在方法名称中包含try

因此,如果我们使用try方法,我们只会得到一个布尔结果。

因此,以下代码将不会引发任何异常:

string input = "blabla";
int number;
if (int.TryParse(input, out number))
{
// wooohooo we got an int!
} else
{
//dooh!
}

然而,这段代码可能会(在这种情况下将会)抛出异常:

string input = "blabla";
int number;
try
{
     number = int.Parse(input); //throws an exception
}
catch (Exception)
{
     //dooh!
}

使用Try方法是一种更安全和更防御性的编码方式。 如果不是整数,代码片段#2执行起来需要更多的性能。

如果你想让它在这个上下文中更有意义,你的代码片段#2应该写成int number = int.Parse(input); - Pierre Arnaud
在 try 块之前,您仍然缺少 int number; 声明以及 number = ... 赋值。 - Pierre Arnaud
@PierreArnaud 谢谢,我现在也包括了'int number'。 - Fabian Bigler
1
请注意,如果异常与正在执行的直接操作有些不相关,例如 TryLoadFile(path, out file) 内存不足。因此,调用者期望在路径错误或访问被拒绝时不会出现错误,但是对于可能出现的更奇怪的问题,会抛出异常。并且要进行文档记录。 - Luke Puplett

6

当您想表明方法调用可能产生无效结果时,必须在方法名中使用“Try”。按照.NET标准,这实际上不是引发异常的函数,而是从程序角度返回一些VALIDNON_VALID值的函数。

最后,这完全取决于您的团队决定使用的命名约定。


-1
TLDR: 我要试试这个,因为这个简单的问题让我停下来思考了很多:
当只有一个明显的失败原因时,返回一个表示成功的布尔值的方法应该只被命名为'TryX'。
解释
我认为我们必须考虑到这个有趣的观点。微软所命名的TryParse模式是有特定原因的 - 使用'TryX'可能被认为是一种反模式,他们正试图避免其扩展的使用。
如果Tester-Doer模式是先测试再执行,那么执行再测试的模式可能被称为Doer-Tester。然而,有一个根本的区别。在Tester-Doer中,执行者在失败时仍会抛出异常。而在这个TryX Doer-Tester中则不是这样。
这是一个“成功的坑”问题。现在你有一段代码,可能什么都不做。最好的情况是你不知道它为什么失败。如果忘记了if/测试,就会在代码的更深处产生奇怪的红鱼问题。
我不怕承认这只是猜测。但当我第一次看到“TryParse”作为一个模式名称时,我想他们怎么能这么懒呢...然后我想也许他们并不懒。
使用解析(parse),只有一个原因会导致异常抛出-输入字符串格式不正确。因此,可以理解返回值为false的含义。
但在一般情况下,不能这么说-可能有多个失败原因。你很快就会涉及到其他最佳实践考虑和辩论,比如抛出异常的情况最小化try/catch块及相关日志记录
我并不是说这种模式从来没有应用的机会。我想在后台线程、关闭或记录尝试期间,如果出现问题,你的恢复选项确实变得很少。但是真的没有任何你可以做的吗?我认为这是我们在依赖一个已经建立的模式之前应该问的问题,这个模式让我们能够编写可能或可能不会完成其预期功能的方法。
最后,我想用一个新的“Yoda”规则来结束。- 要么做,要么不做,没有尝试这一说。

-2

下面是 Uncle Bob 在他的书籍《Clean Code》中提供的一个示例。每当我们期望抛出异常时,我们可以在方法名称前使用 Try 前缀:

public void sendShutDown()
{
    try{
        tryToShutDown();
    } catch (DeviceShutDownError e) {
        logger.log(e);            
    }
}

然后(改编):

private void tryToShutDown()
{
    //some code with no error handling, but
    //something might go wrong here
}

tryToShutDown 方法不进行任何错误处理,因为这是 sendShutDown 方法的责任。

微软的 TryParse 模式违反了干净代码指南,该指南建议我们避免使用输出参数。

如果我们不是在开发 C# 的新版本,就不必坚持所有微软的指南。有时它们并不是最好的选择。


这篇答案提供了很好的建议,告诉我们当 Microsoft 的 Try-Parse 模式不适用于某种情况时该怎么做。遗憾的是,在 .NET 中以 Try...() 命名的方法暗示了 Try-Parse,因为有很多情况需要尝试解析之外的其他操作。 - Theophilus

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