2008-10-08 135 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 */ 
} 
相關問題