声明一个总是抛出异常的方法?

Posted

技术标签:

【中文标题】声明一个总是抛出异常的方法?【英文标题】:Declare a method that always throws an exception? 【发布时间】:2011-04-23 00:26:32 【问题描述】:

我有一个类似的方法:

int f() 
  try 
    int i = process();
    return i;
   catch(Exception ex) 
    ThrowSpecificFault(ex);
  

这会产生编译器错误,“并非所有代码路径都返回值”。但在我的情况下 ThrowSpecificFault() 将始终抛出(适当的)异常。所以我不得不在最后加上一个返回值,但这很丑。

首先,这种模式的目的是因为“process()”是对外部 Web 服务的调用,但需要转换各种不同的异常以匹配客户端的预期接口(我想是外观模式)。

有什么更简洁的方法吗?

【问题讨论】:

相关:Is there a standard “never returns” attribute for C# functions ? 【参考方案1】:

我建议你把ThrowSpecificFault(ex)转换成throw SpecificFault(ex)SpecificFault 方法将返回要抛出的异常对象,而不是自己抛出。干净多了。

这是Microsoft's guidelines推荐的模式。

【讨论】:

+1,这是微软指南中推荐的模式。 我愿意:msdn.microsoft.com/en-us/library/seyhszts.aspx;找到文本“使用异常生成器方法”。 这是一个真正的解决方案。在我看来,其他一切都只是绒毛。 我也有同样的问题,这个选项无效。因为在我的例子中,“ThrowSpecificFault”是一个之前已经定义过并且有一些数据的对象。所以我希望那个对象用他拥有的数据抛出异常。 不幸的是,这是一个简单的答案,通常可以工作(并且适用于简单的示例),但如果您每次都尝试进行一些日志记录并试图做好并将其包装起来,那么完全失败一个可重用的函数。【参考方案2】:

现在返回类型可以是类型,或者“void”表示“无返回类型”。理论上,我们可以添加第二个特殊返回类型“never”,它具有您想要的语义。由对“never”返回方法的调用组成的表达式语句的端点将被认为是不可达的,因此在 C# 中“goto”、“throw”或“return”是合法的每个上下文中都是合法的.

十年后的现在,这不太可能被添加到类型系统中。下次从头设计类型系统时,请记住包含“从不”类型。

【讨论】:

【参考方案3】:

这里的问题是,如果您进入f() 中的catch 块,您的函数将永远不会返回值。这将导致错误,因为您将函数声明为int,这意味着您告诉编译器您的方法将返回一个整数。

以下代码将执行您要查找的操作并始终返回一个整数。

int f() 
  int i = 0;
  try 
    i = process();

   catch(Exception ex) 
    ThrowSpecificFault(ex);
  
  return i;

将 return 语句放在函数的末尾,你会没事的。

无论您的应用程序经过何种执行路径,确保您的方法始终返回一个值始终是一个好主意。

【讨论】:

虽然这可行,但这段代码并不理想,因为它可能会诱使读者认为即使process() 抛出异常也会返回i。最好在catch 块中使用throw 语句。【参考方案4】:

你可以这样做:

catch (Exception ex)

    Exception e = CreateSpecificFault(ex);
    throw e;

【讨论】:

【参考方案5】:

没有。

想象一下,如果ThrowSpecificFault 是在单独的 DLL 中定义的。 如果修改 DLL 使其不抛出异常,然后在不重新编译的情况下运行程序,会发生什么?

【讨论】:

想象一下,如果方法 Foo 是在单独的 DLL 中定义的。如果修改 Foo 以返回 long 而不是 int,然后运行调用 Foo 而不重新编译它的程序,会发生什么? 没什么好。在外部库中更改方法的签名然后在不重新编译的情况下继续使用它是永远正确的。这就是为什么我们在程序集上有版本标记等。 @Eric - 我相信 SLaks 的假设情况不需要更改签名,因此这不是明显的重大更改。 @kvb:据我所知,提议的功能是捕捉一个方法永远不会返回在其签名中这一事实。 @Eric:虽然该功能可以解决 OPs 问题,但我没有得到 OP 专门寻找该功能的印象,而是没有意识到为什么它是一个问题。我认为 SLak 的观点是,由于签名并未表明该方法永远不会返回,因此 OP 的请求是不可能的。如果存在“从不”返回类型,则 OP 将更改 ThrowSpecificFault 的签名并完成。 不能将属性添加到框架中,将方法标记为“总是抛出”吗?如果该方法发生变化以至于它并不总是抛出,它会产生一个编译器错误。【参考方案6】:

您有三个选择:

总是返回 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);
    

或者创建一个自定义的异常类并抛出它:

int f() 
    try 
        int i = process();
        return i;
     catch(Exception ex) 
        throw new SpecificFault(ex);
    

【讨论】:

【参考方案7】:

由于 .Net Standard 2.1 和 .Net Core 3.0,您可以使用 [DoesNotReturn] attribute。 这是 Stephen Toub 的 Proposal: [DoesNotReturn]。

很遗憾,但没有不幸的内部,这可以与 [StackTraceHidden] 结合使用。这可能会在Consider exposing System.Diagnostics.StackTraceHiddenAttribute publicly 的帮助下改变。

已编辑:感谢 Stephen Toub,[StackTraceHidden] 现在是 .Net 6.0 的一部分。

【讨论】:

【参考方案8】:

怎么样:

int f() 
 int i = -1;
 try 
   i = process();       
  catch(Exception ex) 
   ThrowSpecificFault(ex);
 
 return i;

【讨论】:

我将此作为答案,但 Robert Greiner 击败了我。【参考方案9】:

@MuiBienCarlota 适合我,因为我正在做一些日志记录。

类似的东西

[DoesNotReturn]
protected void LogAndThrow(Exception ex)

    _log.LogError(ex, ex.Message);
    throw ex;

【讨论】:

【参考方案10】:

是的。

不要期望 ThrowSpecificFault() 抛出异常。让它返回异常,然后把它扔在这里。

它实际上也更有意义。您不会对“正常”流程使用异常,因此如果您每次都抛出异常,异常就会成为规则。在函数中创建具体的异常,在这里抛出,因为这里是流程的异常..

【讨论】:

【参考方案11】:

我想你可以让 ThrowSpecificFault 返回一个 Object,然后你可以

return ThrowSpecificFault(ex)

否则,您可以将 ThrowSpecificFault 重写为 Exception 子类型的构造函数,或者您可以将 ThrowSpecificFault 设置为创建异常但不抛出异常的工厂。

【讨论】:

【参考方案12】:

在您的情况下,那是您的知识而不是编译器。现在有办法说这种方法肯定会抛出一些讨厌的异常。

试试这个

int f() 
  try 
    return process();
   catch(Exception ex) 
    ThrowSpecificFault(ex);
  
  return -1;

你也可以使用 throw 关键字

int f() 
  try 
    return process();
   catch(Exception ex) 
    throw ThrowSpecificFault(ex);
  

但是那个方法应该返回一些异常而不是抛出它。

【讨论】:

【参考方案13】:

使用 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:

http://msdn.microsoft.com/en-us/library/ff646991.aspx

【讨论】:

以上是关于声明一个总是抛出异常的方法?的主要内容,如果未能解决你的问题,请参考以下文章

Java中的异常处理:何时抛出异常,何时捕获异常?

Java中的异常的捕获和抛出是啥意思,有啥区别

Java程序中的异常应该在啥时候抛出

从Java向MySQL添加数据抛出异常

java-异常处理和线程的一些简单方法及使用

java 异常