【问题标题】:Is there a name for this "pattern"?这个“模式”有名字吗?
【发布时间】:2011-07-27 04:45:02
【问题描述】:

我想知道这个“模式”是否有一个名称,其中方法签名称为 TrySomething,例如int.TryParsedecimal.TryParse

我的一个同事经常使用这种命名约定——他们不会返回值或抛出异常,而是调用方法TryDoSomething,如果在处理过程中捕获到异常,则会通过 out 参数返回。

编辑:我理解私有示例不是 TryParse 方法的工作方式。这就是发布这个的目的......我不知道该怎么称呼它。我同意这似乎更像是一种命名约定,而不是一种模式。感谢您的所有意见。

编辑:Interesting...

对于可能在常见情况下抛出异常的成员考虑 TryParse 模式,以避免与异常相关的性能问题。

要实现 TryParse 模式,您需要提供两种不同的方法来执行在常见场景中可能引发异常的操作。第一个方法 X 执行操作并在适当时抛出异常。第二种方法 TryX 不会抛出异常,而是返回一个指示成功或失败的布尔值。成功调用 TryX 返回的任何数据都使用 out(Visual Basic 中的 ByRef)参数返回。 Parse 和 TryParse 方法就是这种模式的示例。

【问题讨论】:

  • 我认为命名是一种约定,而不是一种模式。
  • 如果他包装了一个普通方法,可以在 TryXYZ 方法内抛出异常,该方法捕获异常并通过 out 参数将其返回,那么我将这种模式称为:愚蠢。
  • "如果在处理过程中捕获到异常,它将通过 out 参数返回" 这不是 int.TryParsedecimal.TryParse 的工作方式。
  • @Greg 我明白了,我只是指整个 TrySomething 的想法。异常/输出参数就是他的编码方式。
  • 更多信息和替代方案:msdn.microsoft.com/en-us/library/ms229009.aspx

标签: c# design-patterns


【解决方案1】:

我可能会称之为Error Hiding Pattern。如果您的代码通常会产生异常,而该异常会用布尔值提前终止,那么您可以从创建 TryX 中受益。如果您查看框架提供的方法,您会注意到TryX 变体存在,只要它不平凡或容易出错(或者经常这样做,它应该在框架中)编写您自己的IsValidX方法。

如果您必须捕获异常,则将其包装在方法中是没有意义的。您所做的只是让调试输入问题变得更加困难。用户可能只会看到失败方法的副作用,而不是有一个很好的堆栈跟踪来跟踪输入失败。更糟糕的是,在调试手头的问题时,开发人员可能不得不重新创建钝的程序状态以影响故障模式。

基本上,如果先验您知道操作会因异常而失败,那么提供伴随TryX 操作是合乎逻辑且适当的。这就是所谓的 Tester-Doer 模式。 事后对操作的分析不是Tester-Doer,而只是简单的异常处理

【讨论】:

    【解决方案2】:

    In this post about TryParse 他们称之为 Tester-Doer 模式。

    【讨论】:

    • Tester-Doer 在操作将/不会失败的先验知识上工作。使用异常是事后知识。
    【解决方案3】:

    首先,如果您的描述是准确的,那么 int.TryParse 及其兄弟姐妹根本不是这样工作的。

    我承认,在我看来,这些方法有些问题,因为它们没有将解析失败的原因传达给调用者,只是告诉调用者失败了。在这方面,我很乐意看到更好的方法来处理这个问题。

    事实上,在我看来,一个更好的方法是我在第三方库中看到的,但我不记得了,但基本上他们有各种自定义类型,它们有这样一对 Parse/TryParse 方法,并且他们做了以下事情:

    1. 定义一个通用结构,其中包含通过解析字符串获得的值以及传达解析结果的枚举类型的值
    2. 所有 TryParse 方法都返回此结构,并且没有 out 参数
    3. Parse 方法简单地调用 TryParse 方法,然后将该枚举的各种不成功结果转换为适当的异常

    在我看来,这里的主要问题是它不可扩展。如果我想使用他们的系统和类型,并添加我自己的原因,我不能这样做,但其中的一些变体可以轻松处理。

    无论如何,int.TryParse 方法根本不会在内部抛出任何异常。相反,他们会通过实际解析字符串的动作,如果遇到无法处理的问题,他们只会返回 false,仅此而已,没有例外。

    异常处理比没有异常的替代方法稍微贵一些,这就是为什么一些这样的核心方法针对性能进行了优化。

    这就是为什么在我的评论中我称你的模式,如果准确的话,是愚蠢的,因为你已经将异常处理与 out 参数结合起来。检索到该异常后,您可以做什么?丢它?然后你又回到了第一格。

    我会认真研究你的模式并尝试改变它。

    当然,所有这些都取决于您对它的描述是准确的假设。

    【讨论】:

    • +1,我喜欢框架中丰富的数字/日期解析和验证库。 “因为 Y,我停止了从 X 到 X + n 的处理。”
    【解决方案4】:

    你可以称之为ExceptionSafeBridge。它将您从异常世界带入错误代码世界。但我不认为这是官方的。

    这种模式 在您需要在例如C 代码和托管代码。

    【讨论】:

      【解决方案5】:

      我称之为“希望我有Option<T>”模式(类似于“希望我有Either<T,E>”模式——想象一下E:异常)。

      TryXYZ(在上面的示例中)使用布尔结果和out 参数模拟 Option。 (对于值类型,在这种情况下它可能是 Nullable<T> ——我怀疑 int.TryParse 和朋友不是这样的,部分原因是 Nullable 出现在 .NET 中要晚得多)。通过out 返回异常更类似于Either。

      在 C# 中,我一般不建议捕获异常只是为了将它们传递给 out 参数(这个“规则”可能与支持区分联合和模式匹配的语言不同)——我尝试 1 ) “正确”地处理它,但它已被定义,可能特殊情况下out;或 2) 放开它,以便调用者可以尝试相同的操作。

      (正如其他人所指出的,这更像是一种约定)。

      编码愉快。

      【讨论】:

      • @user166390 感谢您的一些好主意...我喜欢您的 Either 概念。我将使用类似的东西。
      【解决方案6】:

      设计模式通常指的是与语言无关的一般概念,所以不,这不是模式。这可能是一个成语,但很可能只是一个命名约定,正如 Aaron 在评论中正确提到的那样。

      【讨论】:

        【解决方案7】:

        是的。它被称为Tester-Doer 模式。

        【讨论】:

        • Tester-Doer 在操作将/不会失败的先验知识上工作。使用异常是事后的知识。
        【解决方案8】:

        如果在 tryparse 期间出现问题,即

         int val;
                if(int.TryParse("2", out val))
                {
                    //do work with val
                }
        

        那么你就不会通过 out 参数捕获异常,值为 0 表示 out,false 作为布尔值返回。

        最好的办法是改用“is”或“as”。

        虽然我不会真正称您描述模式的方法...只是一种编码实践(不是最好的)。

        例如:

        private void SetObj(object obj)
        {
            int thisInt = obj as int;
            if(thisInt != null)
            {
               //do work
            }
            else
            {
               //handle issue
            }
        
        }
        

        上面的在运行时比 try/catch 效率高很多。

        如果您在 as 对您不可用时使用“is”(“as”并非在所有类型上都可用),请确保不要通过实现 as WITH is 来增加冗余...仅使用其中一个。

        【讨论】:

        • “是还是作为”?你这是什么意思?
        猜你喜欢
        • 1970-01-01
        • 2011-04-23
        • 2021-06-18
        • 2014-03-05
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多