在处理网络和 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:我还没弄清楚是否有可能将一个这样的块包装到另一个块中,否则现在,结果是,当内部块被中止时,外部块再次进行处理。