“TryParse / Parse like”模式:实现它的最佳方式是什么

时间:2008-10-08 12:18:23

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

这个问题是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不能太贵吗?有没有办法在第二个例子中捕获异常?

4 个答案:

答案 0 :(得分:14)

让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");
    }
}

在你承诺之前计算时间!

答案 1 :(得分:3)

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

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 */
}

答案 2 :(得分:2)

第一个例子是正确的,如果您只是要捕获异常而不做任何事情,只是返回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;      
}

如果您不希望将TryExome与throwException暴露给公众,您可以将其作为私人成员。

异常可能会变得昂贵,你可以对字符串进行一些RegEx检查以防止被抛出。这取决于你想要做什么。

答案 3 :(得分:2)

假设这是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),在我看来最好与语言/环境保持一致。