【问题标题】:Best practices for when to use the Try pattern in C#在 C# 中何时使用 Try 模式的最佳实践
【发布时间】:2021-05-10 09:58:33
【问题描述】:

在阅读了一些关于该主题的内容后,我仍然不确定 何时 提供 TryGet*(someParameter, out someValue) 方法。 我理解当存在可以在失败的情况下抛出异常的方法的变体时它是如何有用的,但是我不能真正形成一个通用规则来帮助我决定何时提供任何一种方法.

对此想到的一些问题:

  • 为什么不让非 try 方法不抛出异常?
  • 什么时候抛出异常比只返回 null 更好?
  • 如果决定使用一种仅返回可空类型(或某些结果类型)的方法,是否应该始终反映在方法名称中,因为它必须以“Try”为前缀?
  • 在每种情况下提供这两种方法总是更好吗?

我专门处理一个集合,我有一堆方法,比如: void GetAt(position) bool TryGetAt(position, out item)

我不确定这是否需要,但感觉我所做的在某种程度上是错误的。

【问题讨论】:

  • 请注意,例如TryParse 方法是专门出于性能原因而添加的——虽然抛出异常相对很快,但如果你有一个紧密的循环试图解析数字而失败,那就很重要了。至于写什么——这取决于调用者希望方法做什么。如果调用者期望方法失败,调用TryXXX 方法会更容易。如果调用者希望方法成功,则抛出异常总体上会减少噪音(并让您说出为什么会发生失败)。失败时尽量不要返回null
  • null 可以是方法的有效返回值。所以在出错的情况下返回null是不好的。
  • 您的方法是否应该抛出异常本质上是另一个问题,此处已对其进行了广泛描述:docs.microsoft.com/dotnet/standard/exceptions/…。也看看这个帖子:softwareengineering.stackexchange.com/questions/120355/…
  • 很难知道一个方法是否可以返回 null,从而导致错误或代码臃肿的 null 检查。这已经通过可空引用类型得到缓解,但这是一个相当新的范例。 try 模式是明确的,因此被错误使用的风险较小。

标签: c# .net design-patterns


【解决方案1】:

TryParse 模式不(只是)用于防止异常。这是一种向调用者显示输入可能无效并且他/她可以相应地处理false-case 的方法。所以你从方法中得到两个信息:

  1. 成功时解析/解析的值
  2. bool 指示是否可以成功解析该值

请注意,您无法从值本身获取第二个信息,因为无论您从false-case 中的方法返回什么,它都可能是一个有效值。

TryParse-模式的唯一缺点是返回一个boolout-参数的值,你不能将它与其他方法调用“链接”。这就是为什么我有时会在解析原始类型时提供不同的方式:return Nullable<T>:

public static class StringExtensions
{
    public static int? TryGetInt(this string input)
    {
        return int.TryParse(input, out int value) ? value : new int?();
    }
    // more methods like this...
}

为什么不让非尝试方法不抛出异常?

因为通常您不能这样做,因为异常不是来自您的代码。但正如我所说,它不是为了防止异常,而是为了帮助方法的调用者理解它并处理结果。

什么时候抛出异常比只返回 null 更好?

在不正常程序流程的异常情况下。但是,您应该返回 bool(try-get with out-parameter) 或可为空的类型(原始类型),而不是返回 null

如果决定采用一种方法 返回一个可为空的类型(或一些 Result 类型),这应该总是 反映在方法的名称中,因为它必须加前缀 用“试试”?

是的,这将有助于理解该方法在成功时也会返回信息。

在每种情况下提供这两种方法总是更好吗?

不,这只是迷惑人,应该使用什么方法?!

我正在专门处理一个集合,以及我的 有一堆方法,例如: void GetAt(position) bool TryGet(位置,出项目)

听起来像是TryGet 的合理用例,但不要同时提供两者。但是您没有提供足够的信息。

【讨论】:

  • 感谢您的详细回答。你什么时候提供一种方法而不是另一种?
  • 我也认为TryXXX 也是因为旧的C# 不能返回2 值。我想现在我们可以使用ValueTuple 例如。 (bool isSuccess, int value) TryParse(string val) 和新模式支持异步方法,而 out 版本不支持。为什么不使用Nullable<T> 的原因是因为Nullable<T>Try***() 方法来得晚
  • @TheTall:如果由于参数无效(例如无效格式)而可能失败,则使用Try-pattern,因此如果false 可能是因为输入来自用户。如果它不应该发生,因为它是一个错误,不要使用Try,而是抛出一个异常并在其他地方处理它(正确的日志记录)。
  • @John: 是的,TryParse 被到处使用的原因是在 .NET 2.0 之前不存在可空类型
  • @TimSchmelter @TheTall 尽可能不要使用try{ value = Int32.Parse("abc") } catch{ value=0 } ,因为Exception 很昂贵(它包含堆栈和其他信息)与仅创建int 值相比,成本很高.
【解决方案2】:

对我来说,TryPattern 开始发挥作用,当它经常发生时,你没有匹配,也没有例外。

因此,根据您的具体情况,您的班级被要求提供一些它无法提供的东西的可能性有多大?更重要的是,在这种情况下能做些什么呢?调用者是否可以做一些简单的事情(例如调用其他一些Try-Method,回退到别的东西)或者用无效的参数值调用你的方法真的很特殊,你所能做的就是抛出一个异常退出整个堆栈?

根据每个案例中上述问题的答案,我决定走哪条路。

【讨论】:

    猜你喜欢
    • 2013-12-28
    • 2015-12-10
    • 2010-10-01
    • 1970-01-01
    • 2017-10-23
    • 1970-01-01
    • 2013-09-15
    • 2016-02-27
    • 1970-01-01
    相关资源
    最近更新 更多