10

这个问题是如何指示方法不成功的后续问题。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

4 回答 4

16

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

不过,在你承诺之前先计时!

于 2008-10-08T12:37:05.770 回答
6

我通常使用这种模式。取决于内部方法是如何实现的,这是否有意义。如果您必须使用条件 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 */
}
于 2008-10-08T12:34:46.727 回答
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;      
}

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

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

于 2008-10-08T12:27:54.773 回答
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),在我看来,最好与语言/环境保持一致。

于 2008-10-08T12:30:51.227 回答