.NET中的错误通信和恢复方法

我试图在我的C#代码中进行错误通信和恢复,而不使用exception。 举个例子,假设有一个Func A,它可以被Func B或Func C或其他函数调用。 必须设计Func A以保持重用。 (此应用程序有一个不断发展的库,新function将在一段时间内不断添加)

如果Func A无法执行它应该执行的操作,则返回int,其中任何非零值表示失败。 我也想传达失败的原因。 调用者函数可以多种方式使用此信息:

  1. 它可以向用户显示错误消息,
  2. 它可能会显示与其上下文更相关的错误消息
  3. 它本身可能返回一个int值,表示无法进一步激活调用者函数。
  4. 它可能会尝试使用一些智能算法从错误中恢复。

假设其他函数所依赖的任何函数可能需要将多个事物传递给其调用函数以采取适当的操作,包括状态代码,错误消息和指示数据状态的其他变量。 将所有内容作为分隔字符串返回可能不允许调用者函数在不解析字符串的情况下检索信息(这将导致其自身的问题,并且不建议使用)。

唯一的另一种方法是返回包含所有必需信息的成员变量的对象。 这可能会导致太多“状态”对象,因为每个函数都需要具有其状态对象。

我想了解如何以最优雅的方式设计此要求。 请注意,在编码时,Func A可能不知道调用函数是否具有从错误中恢复的智能,因此我不想抛出exception。 此外,我想看看这样的设计是否可行(并且同时优雅)而不使用exception。

如果唯一的方法是使用每个函数的数据对象进行通信,那么它是专业库的编写方式。 可以有通用数据对象吗? 注意将来可能会添加新function,这些function可能具有不同的状态变量,或者支持有关其错误的信息。

还要注意,由于函数的返回值是一个“状态”对象,因此它应该返回的实际数据可能需要作为ref或out参数传递。

这有设计模式吗?

在发布此问题之前,我已阅读以下文章:

http://blogs.msdn.com/b/ricom/archive/2003/12/19/44697.aspx

当没有抛出exception时,try / catch块是否会损害性能?

error handling没有例外

我还阅读了许多其他文章,建议不要使用代码流控制的exception,以及可恢复的错误。 此外,抛出exception有其自身的成本。 此外,如果调用函数想要从每个被调用函数抛出的exception中恢复,它将必须用try catch块包围每个函数调用,因为通用try catch块将不允许从下一行“继续”错误行。

编辑:

一些具体问题:我需要编写一个同步2个不同数据库的应用程序:一个是专有数据库,另一个是SQL Server数据库。 我想在一个单独的层中封装可重用的函数。

function如下:专有应用程序可以有许多数据库。 需要将来自每个数据库的一些信息推送到单个公共SQL Server数据库。 只有当应用程序的GUI处于打开状态并且只能通过XML读取时,才能读取专有应用程序的数据库。

算法是这样的:

  1. 阅读Proprietory应用程序中的开放数据库列表
  2. 对于每个数据库,启动同步过程。
  3. 检查此数据库中当前登录的用户是否具有“同步权限”。 (注意:可以使用不同的用户ID打开每个数据库)。
  4. 从此数据库中读取数据。
  5. 将数据传输到SQL Server
  6. 继续下一个数据库。

在开发此应用程序时,我将编写几个可重用的函数,如ReadUserPermission,ReadListOfDatabases等。

在这种情况下,如果ReadUserPermission发现该权限不存在,则调用者应记录此权限并继续执行下一个打开的数据库。 如果ReadListOfDatabases无法与Proprietory Application建立连接,则调用者应自动启动应用程序等。

那么应该传达哪些错误条件应该是exception,哪些是使用返回码?

请注意,可重用function可用于其他项目,其中调用者可能具有不同的错误恢复要求或function,因此必须牢记这一点。

编辑:

对于所有提倡exception的人,我问他们:如果Func A调用Func B,C,D,E,F,G和Func B在某些错误条件下抛出exception,但Func A可以从此错误中恢复并希望继续其余的执行即调用Func B,C,D,…,exception处理如何允许“优雅地”执行此操作? 唯一的解决方案是在try catch块中包含对B,C,D,…中的每一个的调用,以便执行剩余的语句。

另请阅读这两条评论:

https://stackoverflow.com/a/1279137/1113579

https://stackoverflow.com/a/1272547/1113579

注意我不反对使用exception,如果可以优雅地实现错误恢复和剩余代码执行而不影响性能。 此外,轻微的性能影响不是一个问题,但我更喜欢设计应该可扩展和优雅。

编辑:

好的,基于“Zdeslav Vojkovic”评论’,我现在正考虑使用例外。

如果我要使用exception,是否可以在不使用exception时给出一些用例,但是使用返回码? 注意:我说的是返回代码 ,而不是函数应该返回的数据。 是否有使用返回代码表示成功/失败或没有用例的用例? 这将有助于我更好地理解。

我从“Zdeslav Vojkovic”中理解的一个例外情况是当被调用者函数想要强制通知调用函数某些条件并中断调用者执行时。 在没有例外的情况下,呼叫者可能会也可能不会选择检查返回码。 但是在exception情况下,调用函数必须处理exception, 如果它想继续执行

编辑:

我有另一个有趣的想法。 任何想要支持调用函数从其错误中恢复的想法的被调用者函数都可以引发事件,并在事件处理完毕后检查事件数据,然后决定是否抛出exception。 根本不会使用错误代码。 例外情况将用于未恢复的错误。 基本上,当被调用函数无法执行其合约所述的内容时,它会以任何可用事件处理程序的forms请求“帮助”。 如果它无法执行合同,它会引发exception。 优点是减少了抛出exception的额外开销,并且仅当被调用函数或其任何调用函数无法从错误中恢复时才抛出exception。

假设调用者函数不想处理错误,而调用者的调用函数想要处理错误,则自定义事件调度程序将确保以事件注册的相反顺序调用事件处理程序,即最近注册的事件应该在其他已注册的事件处理程序之前调用handler,如果此事件处理程序能够解决该错误,则根本不会调用后续的事件处理程序。 另一方面,如果最新的事件处理程序无法解决错误,则事件链将传播到下一个处理程序。

请对此方法提供反馈。

如何将一个常见的FunctionResult对象用作你不希望抛出exception的所有方法的out参数?

 public class FuncResultInfo { public bool ExecutionSuccess { get; set; } public string ErrorCode { get; set; } public ErrorEnum Error { get; set; } public string CustomErrorMessage { get; set; } public FuncResultInfo() { this.ExecutionSuccess = true; } public enum ErrorEnum { ErrorFoo, ErrorBar, } } public static class Factory { public static int GetNewestItemId(out FuncResultInfo funcResInfo) { var i = 0; funcResInfo = new FuncResultInfo(); if (true) // whatever you are doing to decide if the function fails { funcResInfo.Error = FuncResultInfo.ErrorEnum.ErrorFoo; funcResInfo.ErrorCode = "234"; funcResInfo.CustomErrorMessage = "Er mah gawds, it done blewed up!"; } else { i = 5; // whatever. } return i; } } 

确保所有可以在没有exception情况下失败的函数都具有FuncResultInfo参数

“这是专业图书馆的写作方式吗?”

不,通过使用exception进行error handling来编写专业库 – 我不确定是否存在使用建议方法的模式,但我认为它是反模式(在.NET中)。 毕竟,.NET本身是一个专业框架,它使用exception 。 此外,.NET开发人员习惯于例外。 你认为你的库真的那么特别强迫用户学习完全不同的error handling方式吗?

你刚刚做的是重新发明COMerror handling。 如果这是你想要做的,那么检查一下这个和ISupportErrorInfo接口的一些想法。

你为什么要这样做? 我敢打赌这是一个表现“优化”。

对exception处理的性能问题的恐惧几乎总是不成熟的优化。 你将创建一个尴尬的API,其中每个返回值必须通过ref / out参数处理,这将损害你的lib的每个用户,只是为了解决可能根本不存在的问题。

“Func A可能不知道调用函数是否具有从错误中恢复的智能,所以我不想抛出exception”

所以你想确保调用者默默地允许FuncA搞乱系统不变量,并且调用者只是快乐地继续下去? 这将使调试看似不可能的错误变得更加困难,因为这会在以后的另一个函数中发生。

在某些情况下,避免exception是有意义的,但应该有一个很好的理由。 例外是个好主意。

编辑:我看到你已经添加了“你还阅读了许多其他文章,建议不要使用代码流控制的exception”。 这是正确的,.NET中的exception不是代码流,而是error handling。

你问:

如果Func A调用Func B,C,D,E,F并且它必须使用try catch封装每个调用,因为它可以从错误中恢复,或者它仍然希望执行剩余的函数调用,那么尝试catch语句就不那么多了尴尬

不过是另类。 你犯了一个错误,你可以简单地处理从函数返回的所有错误,但通常不能。

考虑是否需要单独处理每个函数 – 最坏的情况和代码通常不是这样编写的:

 Result x, y; try { x = Function1(); } catch(SomeException e) { // handle error } try { y = Function2(); } catch(SomeOtherException e) { // handle error } 

反对:

 int error; Result x, y; error = Function1(out x); if(error != SOME_KNOWN_ISSUE) { // handle error } error = Function2(out y); if(error != SOME_KNOWN_ISSUE) { // handle error } 

没有太大的区别。 请不要告诉我你不会检查错误代码。 但是,如果您决定忽略所有错误(一个可怕的想法),那么exception就更简单了:

 try { var x = Function1(); var y = Function2(); var z = Function3(); } catch Exception() { you still can see the message here and possibly rethrow } 

VS

 Result1 r1; Function1(out r1); Result2 r2; Function2(out r2); Result3 r3; Function3(out r3); // and here you still don't know whether there was an error 

你能详细说明“我需要关于时间限制的可预测性”是什么意思吗?

在某些系统级软件或实时内容中,您无法负担与exception处理相关的堆栈展开,因为您无法保证持续时间,这可能违反您的时序要求。 但在.NET中情况绝对不是这样,因为垃圾收集在这方面要差得多。

此外,当您说“在.NET中我总是使用exception进行error handling”时,您能解释一下如何定义错误条件或将其定义为什么? 可恢复情况是错误情况还是错误情况? –

@shambulater已经在评论中给出了一个很好的例子。 在FileStream ,丢失的文件不可恢复,它将抛出。 在FileStream的客户端中,它可能是可恢复的,具体取决于上下文。 有些客户会忽略它,有些客户会退出应用程序,有些会将其包装在另一个例外中,让上游某人决定。

你什么时候不使用例外?

在那些我也不会返回错误代码的情况下。

我在ms-access中广泛使用FunctionResult方法,它运行得非常好。 我认为它远比error handling好。 首先,每条错误消息都是特定于应用程序的,并不是通常关闭目标的默认错误消息。 如果错误传播了函数的调用列表,则错误消息可以菊花链式连接在一起。 此最终错误消息看起来像一个调用堆栈,但更清洁,例如[无法读取驱动器F中的照片:,无法读取文件,驱动器未准备就绪]。 Wacko,我刚刚发现一些驱动器可以安装但没有准备好。 我无法对该错误进行unit testing,因为我不知道可能发生这样的错误(意味着SD卡读卡器为空)。 即使没有先前对此错误的了解,我也可以编写一个优雅地处理它的应用程序。

我的方法是在一个类中调用一个方法,该类被编写为一个返回布尔值的函数。 返回值在函数的最后一行设置为True,因此如果函数在最后一行之前退出,则默认情况下不成功。 我编码,调用函数看起来像getphotos(folderID)然后…做某事..否则报告错误。 类模块内部是模块级错误变量(Str mEM),它通过getter读取,因此该类具有.em属性,用于保存错误消息。 我还有一个注释变量,有时像错误信息一样使用,例如,如果文件夹为空,寻找照片的代码有效,但没有返回任何照片。 这不会是一个错误,但我可能想要与调用程序进行通信。 如果出现错误,用户将收到错误消息,并且调用过程将退出。 相比之下,如果有一个cmt,例如“没有照片”,那么我可能会尝试阅读照片元数据。 Zdeslav Vojkovic如何处理像这样的子句?

我正在转向C#,因此找到了这个post。 我喜欢知道为什么函数调用失败的确定性(我一直与数据库和文件系统交互,所以我很难用unit testing来覆盖我的项目)。 我同意Zdeslav Vojkovic关于使用其使用的标准的exception,但不会在我自己的代码中这样做。 我正在寻找一个干净的设计模式,它允许我validation被调用函数内的参数,并通知调用者参数是否正确。