33

我有这样的方法:

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

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

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

任何更清洁的方法来做到这一点?

4

13 回答 13

71

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

这是Microsoft 指南推荐的模式。

于 2010-10-08T17:41:27.427 回答
9

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

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

于 2010-10-08T19:37:15.857 回答
8

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

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

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

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

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

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

于 2010-10-08T17:40:30.580 回答
5

你可以这样做:

catch (Exception ex)
{
    Exception e = CreateSpecificFault(ex);
    throw e;
}
于 2010-10-08T17:42:41.650 回答
3

不。

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

于 2010-10-08T17:40:17.587 回答
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);
    }
}
于 2010-10-08T17:41:28.850 回答
2

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

可惜没有不幸的internal,这可以与 [StackTraceHidden] 结合使用。这可能会在考虑System.Diagnostics.StackTraceHiddenAttribute公开曝光的帮助下改变。

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

于 2019-12-04T13:25:26.103 回答
1

怎么样:

int f() {
 int i = -1;
 try {
   i = process();       
 } catch(Exception ex) {
   ThrowSpecificFault(ex);
 }
 return i;
}
于 2010-10-08T17:41:18.633 回答
1

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

像这样的东西

[DoesNotReturn]
protected void LogAndThrow(Exception ex)
{
    _log.LogError(ex, ex.Message);
    throw ex;
}
于 2021-10-04T07:58:52.750 回答
0

是的。

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

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

于 2010-10-08T17:41:36.870 回答
0

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

return ThrowSpecificFault(ex)

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

于 2010-10-08T17:41:48.740 回答
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);
  }
}

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

于 2010-10-08T17:44:41.490 回答
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 的更多信息:

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

于 2010-10-08T17:50:49.870 回答