在处理网络和 IO 时,通常存在即使对于用户(尽管是有经验的用户)也很容易解决的限制,但很难提前了解它们。繁忙的文件,不稳定的连接 - 我经常错过重复按钮,尤其是当整个过程需要并回滚时。
到目前为止,我已经为这个问题实施了一个或多或少通用的解决方案:
public enum ExceptionHandle
{
Abort,
Retry,
Ignore
}
public class ExceptionEventArgs
{
public Exception Exception { get; }
public ExceptionHandle? Handled { get; set; }
public ExceptionEventArgs(Exception ex)
{
this.Exception = ex;
}
}
public static class ExceptionHandler
{
public static event EventHandler<ExceptionEventArgs> Handler;
public static void TryExecute(Action action)
{
TryExecute(() => { action(); return true; }, false);
}
public static T TryExecute<T>(Func<T> action, T whenIgnored)
{
ExceptionHandle? handled = ExceptionHandle.Retry;
while (handled == ExceptionHandle.Retry)
{
try
{
return action();
}
catch (Exception ex)
{
handled = OnHandler(new ExceptionEventArgs(ex));
if (handled.HasValue)
{
switch (handled.Value)
{
case ExceptionHandle.Abort:
throw;
break;
case ExceptionHandle.Retry:
break;
case ExceptionHandle.Ignore:
break;
default:
throw new ArgumentOutOfRangeException();
}
}
else
{
throw;
}
}
}
return whenIgnored;
}
private static ExceptionHandle? OnHandler(ExceptionEventArgs e)
{
if (Handler == null || !Handler.GetInvocationList().Any())
{
ExceptionDispatchInfo.Capture(e.Exception).Throw();
}
else
{
Handler.Invoke(null, e);
}
return e.Handled;
}
}
因此,任何订户ExceptionHandler.Handler都可以自动解决问题,或者将解决方案转交给用户。现在可以包装任何危险代码:
var tested = ExceptionHandler.TryExecute(() =>
{
using (var destination = new MemoryStream())
{
using (Stream stream = entry.Open())
stream.CopyTo(destination);
return destination.Length == entry.Length;
}
}, false);
总的来说,目前的实施在我看来已经可以忍受并且有效。但是,我怀疑这样的解决方案已经存在于某个地方,我只是找不到它们。有人可以建议从哪里获得或至少查看现成的解决方案吗?好吧,如果我的代码中有门框,我也不会拒绝帮助。
UPD:是的,我知道即使这样也有问题的情况 - 操作可以是一次性的(关闭连接,破坏 sql 会话,无论你想做什么)。它已经留在使用代码的人的良心上。虽然,我也会为这个问题寻找有趣的选项,但你会限制相同的无花果。
UPD2:我还没弄清楚是否有可能将一个这样的块包装到另一个块中,否则现在,结果是,当内部块被中止时,外部块再次进行处理。
“我不是医生,但我能看见” (C)
在网上找到了完成的方案,看起来还不错:
有一种使用PostSharp的方法。
该解决方案是在示例的基础上开发的:示例 1和示例 2。
优点:
缺陷:
CurrentExceptionHandler.HandlerFunc中。这是拐杖。编码:
使用示例:
要使用自定义异常处理程序,您必须将其设置为静态属性的值
CurrentExceptionHandler.HandlerFunc。我不认为这个问题有一个通用的解决方案。由于您编写的代码本质上是“与生活本身一样多样化”的业务逻辑,因此您将无法提前涵盖所有可能的情况。
副手代码有什么问题:
ExceptionHandler'a 的静态实例,这意味着您必须同时订阅多个处理程序。在这种情况下,需要一种机制来决定给定的处理程序是否对给定的错误负责。你没有这个逻辑,结果会很复杂。此外,一旦您尝试使其变得更复杂,通用对象就应该会遇到多线程访问问题。在我看来,您不应该尝试构建通用逻辑,这是行不通的,因为有很多情况。为各种场合编写小的辅助函数并将它们收集到实用程序类中会更好、更容易和更有效。
比如这么简单的逻辑
很难用
ExceptionHandler'a.使用这样的测试函数:
但这不是一个通用的功能,它只是一个可以很容易地手工编码的例子。