2008-10-08 132 views
8

此问题是从How to indicate that a method was unsuccessful开始的后续操作。 xxx()Tryxxx()模式在许多库中非常有用。我想知道在不重复我的代码的情况下提供两种实现的最佳方式。“TryParse/Parse like”模式:实现它的最佳方式是什么

什么是最好的:

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太贵了?有没有办法在第二个例子中捕获异常?

+0

只要您的TryX实现不需要捕获异常(即,您的代码生成的异常,而不是代码生成的异常),我会同意您的直觉(如果可能,请避免TryX病例的异常)你打电话)。 – 2008-10-08 12:28:06

+0

此外,我不确定你的意思是“有没有办法在第二个例子中捕捉异常?” - 抓住什么异常?你想抛出DoSomething(尽管你会抛出一个特定的异常,而不是一般的异常)。 – 2008-10-08 12:28:52

+0

@Jonathan:我的意思是“重新抛出发生在内部处理中的异常,调用者可以知道是什么导致了错误” – Luk 2008-10-08 12:31:08

回答

13

使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

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

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

+0

我不会将“throwException”arg的版本作为public;这已经隐含在您选择调用TrySomething vs Something的选项中。不过,这可能是一种私有方法,它同时支持两种公共实现。 – 2008-10-08 12:33:06

+0

在公共方法上有像bool throwException这样的参数是不好的 - 请参阅.NET Framework设计指南。另外,吞并异常并不好:) – nightcoder 2014-01-08 18:04:32

1

假设这是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),在我最好的意见留在语言/环境一致。

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 */ 
} 
相关问题