“TryParse/Parse like”模式:实现它的最佳方式是什么
此问题是从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太贵了?有没有办法在第二个例子中捕获异常?
使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");
}
}
虽然在您付诸实施之前这样做了!
第一个例子是正确的,如果你只是要捕捉异常,而不做任何事情,只是返回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检查以防止引发异常。这取决于你想要做什么。
我不会将“throwException”arg的版本作为public;这已经隐含在您选择调用TrySomething vs Something的选项中。不过,这可能是一种私有方法,它同时支持两种公共实现。 – 2008-10-08 12:33:06
在公共方法上有像bool throwException这样的参数是不好的 - 请参阅.NET Framework设计指南。另外,吞并异常并不好:) – nightcoder 2014-01-08 18:04:32
假设这是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)
,在我最好的意见留在语言/环境一致。
我通常使用这种模式。取决于内部方法是如何实施的,以确定这是否有意义。如果你必须使用条件捕获块,它可能会有点讨厌...
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 */
}
只要您的TryX实现不需要捕获异常(即,您的代码生成的异常,而不是代码生成的异常),我会同意您的直觉(如果可能,请避免TryX病例的异常)你打电话)。 – 2008-10-08 12:28:06
此外,我不确定你的意思是“有没有办法在第二个例子中捕捉异常?” - 抓住什么异常?你想抛出DoSomething(尽管你会抛出一个特定的异常,而不是一般的异常)。 – 2008-10-08 12:28:52
@Jonathan:我的意思是“重新抛出发生在内部处理中的异常,调用者可以知道是什么导致了错误” – Luk 2008-10-08 12:31:08