我有一个类似...的方法。
int f() {
try {
int i = process();
return i;
} catch(Exception ex) {
ThrowSpecificFault(ex);
}
}
这会产生编译器错误,“并非所有代码路径都返回值”。但在我的情况下,ThrowSpecificFault()将始终抛出(相应的)异常。所以我被迫在最后设置一个返回值,但这很难看。
这种模式的目的首先是因为“process()”是对外部Web服务的调用,但需要转换各种不同的异常以匹配客户端的预期接口(我认为是〜外观模式)。
任何更干净的方法吗?
答案 0 :(得分:54)
我建议您将ThrowSpecificFault(ex)
转换为throw SpecificFault(ex)
; SpecificFault
方法将返回要抛出的异常对象而不是自己抛出它。更干净。
这是Microsoft's guidelines推荐的模式(查找文本“使用异常构建器方法”)。
答案 1 :(得分:9)
现在返回类型可以是类型,或“void”意味着“无返回类型”。理论上我们可以添加第二个特殊的返回类型“never”,它具有你想要的语义。包含对“never”返回方法的调用的表达式语句的结束点将被视为无法访问,因此在C#中的每个上下文中都是合法的,其中“goto”,“throw”或“return”是合法的。
现在十年后,这种情况不太可能被添加到类型系统中。下次从头开始设计类型系统时,请记住包含“从不”类型。
答案 2 :(得分:8)
这里的问题是,如果你进入catch
中的f()
块,你的函数永远不会返回一个值。这将导致错误,因为您将函数声明为int
,这意味着您告诉编译器您的方法将返回一个整数。
以下代码将执行您要查找的内容并始终返回整数。
int f() {
int i = 0;
try {
i = process();
} catch(Exception ex) {
ThrowSpecificFault(ex);
}
return i;
}
将return语句放在函数的末尾,你会没事的。
无论您的应用程序通过什么执行路径,确保您的方法始终返回值始终是一个好主意。
答案 3 :(得分:4)
你可以这样做:
catch (Exception ex)
{
Exception e = CreateSpecificFault(ex);
throw e;
}
答案 4 :(得分:3)
没有
想象一下,如果ThrowSpecificFault
是在单独的DLL中定义的。
如果您修改DLL以不抛出异常,那么运行您的程序而不重新编译它会发生什么?
答案 5 :(得分:2)
您有三种选择:
始终返回i但预先声明:
int f() {
int i = 0; // or some other meaningful default
try {
i = process();
} catch(Exception ex) {
ThrowSpecificFault(ex);
}
return i;
}
从方法中返回异常并抛出:
int f() {
try {
int i = process();
return i;
} catch(Exception ex) {
throw GenerateSpecificFaultException(ex);
}
}
或者创建一个自定义的Exception类并抛出:
int f() {
try {
int i = process();
return i;
} catch(Exception ex) {
throw new SpecificFault(ex);
}
}
答案 6 :(得分:1)
怎么样:
int f() {
int i = -1;
try {
i = process();
} catch(Exception ex) {
ThrowSpecificFault(ex);
}
return i;
}
答案 7 :(得分:0)
是
不要指望ThrowSpecificFault()抛出异常。让它返回异常,然后把它扔到这里。
它实际上也更有意义。您不对“正常”流使用异常,因此如果每次都抛出异常,则异常成为规则。在函数中创建特定的异常,并将其抛在此处,因为这是流程的一个例外..
答案 8 :(得分:0)
我想你可以让ThrowSpecificFault返回一个Object,然后你可以
return ThrowSpecificFault(ex)
否则,您可以将ThrowSpecificFault重写为Exception子类型的构造函数,或者您可以将ThrowSpecificFault设置为创建异常但不抛出它的工厂。
答案 9 :(得分:0)
在你的情况下,但那是你的知识而不是编译器。现在可以说这种方法肯定会引发一些令人讨厌的异常。
试试这个
int f() {
try {
return process();
} catch(Exception ex) {
ThrowSpecificFault(ex);
}
return -1;
}
您也可以使用投掷关键字
int f() {
try {
return process();
} catch(Exception ex) {
throw ThrowSpecificFault(ex);
}
}
但是那个方法应该返回一些异常而不是抛出异常。
答案 10 :(得分:0)
使用Unity.Interception清理代码。通过拦截处理,您的代码可能如下所示:
int f()
{
// no need to try-catch any more, here or anywhere else...
int i = process();
return i;
}
您在下一步中需要做的就是定义一个拦截处理程序,您可以自定义异常处理。使用此处理程序,您可以处理应用程序中引发的所有异常。好处是您不再需要使用try-catch块标记所有代码。
public class MyCallHandler : ICallHandler, IDisposable
{
public IMethodReturn Invoke(IMethodInvocation input,
GetNextHandlerDelegate getNext)
{
// call the method
var methodReturn = getNext().Invoke(input, getNext);
// check if an exception was raised.
if (methodReturn.Exception != null)
{
// take the original exception and raise a new (correct) one...
CreateSpecificFault(methodReturn.Exception);
// set the original exception to null to avoid throwing yet another
// exception
methodReturn.Exception = null;
}
// complete the invoke...
return methodReturn;
}
}
将类注册到处理程序可以通过配置文件或以编程方式完成。代码相当简单。注册后,使用Unity实例化对象,如下所示:
var objectToUse = myUnityContainer.Resolve<MyObjectToUse>();
有关Unity.Interception的更多信息:
答案 11 :(得分:0)
从.Net Standard 2.1和.Net Core 3.0开始,您可以使用[DoesNotReturn] attribute。 那是斯蒂芬·图布(Stephen Toub)的Proposal: [DoesNotReturn]。
很可惜,但是如果没有不幸的 internal ,可以将其与[StackTraceHidden]结合使用。在Consider exposing System.Diagnostics.StackTraceHiddenAttribute
publicly的帮助下,情况可能会改变。