【问题标题】:"TryParse / Parse like" pattern: what is the best way to implement it“TryParse / Parse like”模式:实现它的最佳方式是什么
【发布时间】:2008-10-08 12:18:23
【问题描述】:

这个问题是How to indicate that a method was unsuccessful 的后续问题。 xxx() Tryxxx() 模式在许多库中都非常有用。我想知道在不复制代码的情况下提供两种实现的最佳方式是什么。

什么是最好的:

public int DoSomething(string a)
{
     // might throw an exception
}
public bool TrySomething(string a, out result)
{
    try
    {
        result = DoSomething(a)
        return true;
    }
    catch (Exception)
    {
        return false;
    }

public int DoSomething(string a)
{
     int result;
     if (TrySomething(a, out result))
     {
         return result;
     }
     else
     {
         throw Exception(); // which exception?
     }
}
public bool TrySomething(string a, out result)
{
    //...
}

我本能地假设第一个示例更正确(您确切知道发生了哪个异常),但是 try/catch 不会太昂贵吗?有没有办法在第二个例子中捕获异常?

【问题讨论】:

  • 我同意您的直觉(如果可能,请避免 TryX 案例的异常)只要您的 TryX 实现不必捕获异常(即异常是由您的代码,而不是您调用的代码)。
  • 另外,我不确定您所说的“有没有办法在第二个示例中捕获异常?” -- 捕捉到什么异常?你想从 DoSomething 中抛出(尽管你会抛出一个特定于所发生事件的异常,而不是一般异常)。
  • @Jonathan:我的意思是“以调用者知道是什么原因导致错误的方式重新抛出内部处理中发生的异常”

标签: design-patterns return-value try-catch


【解决方案1】:

让 TrySomething 捕获并吞下异常是一个非常糟糕的主意。 TryXXX 模式的一半是为了避免异常对性能的影响。

如果您不需要在异常中提供太多信息,您可以让 DoSomething 方法只调用 TrySomething 并在失败时抛出异常。如果您需要异常中的详细信息,则可能需要更详细的内容。我还没有计算出异常的大部分性能影响在哪里——如果是抛出而不是创建,你可以编写一个私有方法,它与 TrySomething 具有相似的签名,但它返回一个异常或 null:

public int DoSomething(string input)
{
    int ret;
    Exception exception = DoSomethingImpl(input, out ret);
    if (exception != null)
    {
        // Note that you'll lose stack trace accuracy here
        throw exception;
    }
    return ret;
}

public bool TrySomething(string input, out int ret)
{
    Exception exception = DoSomethingImpl(input, out ret);
    return exception == null;
}

private Exception DoSomethingImpl(string input, out int ret)
{
    ret = 0;
    if (input != "bad")
    {
        ret = 5;
        return null;
    }
    else
    {
        return new ArgumentException("Some details");
    }
}

在你承诺之前先计时!

【讨论】:

  • 我喜欢这个实现,我可能会进一步测试它
【解决方案2】:

我通常使用这种模式。取决于内部方法是如何实现的,这是否有意义。如果你必须使用条件 catch 块,它会变得有点讨厌......

public object DoSomething(object input){
  return DoSomethingInternal(input, true);
}

public bool TryDoSomething(object input, out object result){
  result = DoSomethingInternal(input, false);
  return result != null;
}

private object DoSomethingInternal(object input, bool throwOnError){
  /* do your work here; only throw if you cannot proceed and throwOnError is true */
}

【讨论】:

  • 是的,但这意味着如果 throwOnError 为 false,DoSomethingInternal 决不能抛出异常,这可能会使其实施成本高昂,不是吗?
  • 正如我所说,这可能很糟糕,具体取决于 Internal 方法的实现。换句话说,如果您不在调用堆栈的底部并且您调用的方法可能会抛出异常,那么它可能不是最好的模式。
  • 然而,大多数情况下,您会发现您可以避免抛出使用此处其他示例会抛出的异常,从而节省一些堆栈展开时间。
【解决方案3】:

第一个例子是正确的,如果你只是要捕获异常而不做任何事情,而是用它返回 false。

您可以将 TrySomething 更改为如下所示。

public bool TrySomething(string a, out result, bool throwException)
{
  try
  {
    // Whatever
  }
  catch
  {
    if(throwException)
    {
      throw;
    }
    else
    {
      return false;
    }
  }

}

public bool TrySomething(string a, out result)
{
  return TrySomething(a, out result, false);
}

DoSomething 看起来像

public int DoSomething(string a)
{
  int result;

  // This will throw the execption or 
  // change to false to not, or don't use the overloaded one.
  TrySomething(a, out result, true) 

  return result;      
}

如果您不希望带有 throwException 的 TrySomething 公开,您可以将其设为私有成员。

异常可能会变得昂贵,您可以对字符串进行一些正则表达式检查以防止抛出异常。这取决于你想做什么。

【讨论】:

  • 我不会将带有“throwException”参数的版本设为公开;这已经隐含在您选择调用 TrySomething 与 Something 的选择中。不过,作为位于两个公共实现背后的私有方法,这可能没问题。
  • 在公共方法上使用 bool throwException 之类的参数并不好 - 请参阅 .NET Framework 设计指南。另外,吞下异常也不好:)
【解决方案4】:

假设这是C#,我会说第二个例子

public bool TrySomething(string a, out result)
{
    try
    {
        result = DoSomething(a)
        return true;
    }
    catch (Exception)
    {
        return false;
    }
}

它模仿了内置的 int.TryParse(string s, out int result),在我看来,它最好与语言/环境保持一致。

【讨论】:

  • 与界面一致,但不是为了避免异常对性能的影响。它还错误地 (IMO) 吞下了 all 异常,而不仅仅是 DoSomething 通常可能抛出的那些异常。例如,我不希望 OutOfMemoryException 被吞下。
猜你喜欢
  • 2010-12-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-05-25
  • 2021-08-02
  • 1970-01-01
相关资源
最近更新 更多