返回元组<;bool,字符串>;而不是抛出exception()
本文关键字:exception gt lt 元组 bool 返回 字符串 | 更新日期: 2023-09-27 18:28:46
我有一个方法Foo()做一些艰苦的工作,在UI层上我有一个子按钮来调用该方法
如果Foo()方法有问题,我只想调用该方法并显示一个消息框。
我有两种选择来设计方法签名:
1.元组
Tuple<bool, string> Foo()
{
if(!DoHardWorkA()) return Tuple.New(false, "cannot do hardwork A");
if(!DoHardWorkB()) return Tuple.New(false, "cannot do hardwork B");
return Tuple.New(true, String.Empty);
}
2.异常
void Foo()
{
if(!DoHardWorkA()) throw new ProgramSpecificException("cannot do hardwork A");
if(!DoHardWorkB()) throw new ProgramSpecificException("cannot do hardwork B");
return Tuple.New(true, String.Empty);
}
DoHardWorkA()和DoHardWorkB()都是外部方法,我无法控制它们,它们返回true/false表示结果。
从逻辑上讲,我认为我应该选择选项2,因为它们确实是例外;但为了保持一致性,我想选择选项1。
你更喜欢哪一个,为什么?
抛出异常并以一致的方式进行处理会更好。
如果Foo由于任何其他原因失败,那么它也将被处理。假设一个场景。
void UIMethod()
{
Tuple<Result, Error> ret = Foo();
if(ret.Error)
MessageBox.Show(ret.Error);
}
现在,由于需求的变化,您必须在Foo之前调用另一个方法,它也可能引发异常。然后它就变得复杂了。
这样做要容易得多。
void UIMethod()
{
try{
MethodBeforeFoo();
var ret = Foo();
}
catch(Exception ex)
{
MessageBox.Show(ex.Message);
}
}
这实际上取决于您的需要。像这样修改你的代码,它也会处理未处理的异常。
Tuple<bool, string> Foo()
{
try
{
if(!DoHardWorkA()) return Tuple.New(false, "cannot do hardwork A");
if(!DoHardWorkB()) return Tuple.New(false, "cannot do hardwork B");
return Tuple.New(true, String.Empty);
}
catch
{
return Tuple.New(false, "cannot do hardwork A");
}
}
如果你所做的只是调用这些外部方法来完成一些工作,而你正在编写的这个方法是对它们的包装,那么为什么要抛出异常,"处理"方法中的问题并继续,抛出和处理异常会比贵很多很多倍
在您的特定情况下,您所做的只是做一些工作,并显示一个消息框来显示它是否正确执行,所以我选择选项1
请注意,如果您只是捕获异常而不展开堆栈,则成本相当小。只有当您展开堆栈时,它才是昂贵的,如ex.ToString()
或ex.StackTrace
使用异常具有优势,与建议的元组返回值相比,您可以更容易地(*)区分原因。要想弄清楚在使用元组时什么样的东西出错,必须解释字符串值,这很容易出错。使用异常,可以根据异常的类型确定错误的类型。
(*)取决于异常的使用方式——如果你一直抛出一个通用异常,那么就不会有什么不同
当然,您可以在元组中使用整数来指示问题的类型,但错误类型的数值不如异常类型具有描述性(再次假设您没有使用一般类型,如异常)。
我曾经做过的是在成功时返回null或在失败时返回错误消息。异常是不合适的,因为可能会失败,其他开发人员喜欢使用"抛出异常时中断"运行
String Foo()
{
if(!DoHardWorkA()) return "cannot do hardwork A";
if(!DoHardWorkB()) return "cannot do hardwork B";
return null;
}