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

38

我有一个类似这样的方法:

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

这会产生一个编译错误,提示“并非所有的代码路径都返回值”。但在我的情况下,ThrowSpecificFault() 总是会抛出(适当的)异常。因此我被迫在末尾放置一个返回值,但这很丑陋。

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

有没有更简洁的方法来做到这一点?


13个回答

80
我建议您将ThrowSpecificFault(ex)转换为throw SpecificFault(ex); SpecificFault方法将返回要抛出的异常对象,而不是自己抛出。 更加简洁。
这是Microsoft指南推荐的模式。

7
+1,这是微软指南中推荐的模式。 - Joe
2
我做的是:http://msdn.microsoft.com/en-us/library/seyhszts.aspx;找到文本“使用异常生成器方法”。 - CesarGon
3
这是唯一真正的解决方案。在我看来,其他所有东西都只是浮华。 - Dave Van den Eynde
我有同样的问题,而且这个选项是无效的。因为在我的情况下,“ThrowSpecificFault”是一个已经定义并具有一些数据的对象。所以我希望该对象抛出具有其拥有的数据的异常。 - Roee Gavirel
不幸的是,这是一个简单化的答案,通常可以解决问题(并且对于简单的示例有效),但如果您试图每次记录并尝试将其包装在可重用的函数中,则完全失败。 - MBentley

11

现在,返回类型可以是一种类型,或者是"void"(表示没有返回类型)。从理论上讲,我们可以添加第二个特殊的返回类型"never",它具有您想要的语义。由一个调用"never"返回方法组成的表达式语句的终点将被视为不可到达,因此在C#的每个上下文中,使用"goto"、"throw"或"return"合法的地方都可以使用它。

十年后现在加入这个类型系统的可能性非常小。下次从头设计类型系统时,请记得包含一个"never"类型。


8
在这里的问题是,如果您进入 f() 中的 catch 块,您的函数将永远不会返回值。这会导致错误,因为您声明了函数为int,这意味着您告诉编译器您的方法将返回一个整数。
下面的代码将做到您想要的,并始终返回一个整数。
int f() {
  int i = 0;
  try {
    i = process();

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

把return语句放在函数末尾,你就没问题了。

无论应用程序经历什么执行路径,始终确保您的方法都会返回一个值是个好主意。


1
虽然这个代码可以工作,但并不是最理想的,因为它可能会让读者误以为 i 将会在 process() 抛出异常时返回。更好的方法是在 catch 块中使用 throw 语句。 - birgersp

5
你可以这样做:
catch (Exception ex)
{
    Exception e = CreateSpecificFault(ex);
    throw e;
}

4

3
您有三个选项:

始终返回 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);
    }
}

3

不。

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


4
想象一下,如果方法Foo在一个单独的DLL文件中定义。如果你修改Foo的返回值类型从int改为long,然后运行调用Foo的程序而不重新编译它,会发生什么?情况非常糟糕。在外部库中更改方法的签名并继续使用而不重新编译是永远不正确的。这就是为什么我们在程序集上有版本标识等内容的原因。 - Eric Lippert
@Eric - 我认为SLaks的假设情况不需要改变签名,因此它并不像明显的破坏性更改那样。 - kvb
2
@kvb:根据我的理解,所提出的功能是在方法的签名中捕获该方法永远不会返回的事实。 - Eric Lippert
@Eric:虽然这个功能可以解决原帖的问题,但我没有感觉到原帖是在寻找特定的功能,而是没有意识到为什么会有这个问题。我认为SLak的观点是,由于签名没有表明该方法永远不会返回,所以原帖的请求是不可能实现的。如果存在“永不”返回类型,原帖将会改变ThrowSpecificFault的签名并完成任务。 - Brian
能否向框架添加一个属性,将方法标记为“总是抛出”?如果该方法发生更改,使其不再总是抛出,则会生成编译器错误。 - Matt Davis

2

@MuiBienCarlota对我来说是正确的选择,因为我正在进行一些日志记录。

类似这样的东西

[DoesNotReturn]
protected void LogAndThrow(Exception ex)
{
    _log.LogError(ex, ex.Message);
    throw ex;
}

1

这样怎么样:

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

我把这个作为答案留下了,但是Robert Greiner比我先回答了。 - Tony Abrams

0

在你的情况下,那是你的知识而不是编译器的问题。没有办法确切地说这个方法一定会引发一些糟糕的异常。

试试这个

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

你也可以使用 throw 关键字

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

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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接