我从来没有完全满意异常处理的工作方式,有很多异常和try / catch带来的表(堆栈展开等),但它似乎打破了很多OO模型过程
无论如何,这是问题所在:
假设你有一些包装或包含网络文件IO操作的类(例如,在某个特定的UNC路径上读取和写入某个文件)。由于各种原因,您不希望这些IO操作失败,因此如果您检测到它们失败,则重试它们并继续重试它们,直到它们成功或达到超时。我已经有了一个方便的RetryTimer类,我可以实例化它并在重试之间用来休眠当前线程,并确定超时时间已经过去等等。
问题是你在这个类的几个方法中有一堆IO操作,你需要将它们包装在try-catch / retry逻辑中。
以下是一个示例代码段:
RetryTimer fileIORetryTimer = new RetryTimer(TimeSpan.FromHours(10));
bool success = false;
while (!success)
{
try
{
// do some file IO which may succeed or fail
success = true;
}
catch (IOException e)
{
if (fileIORetryTimer.HasExceededRetryTimeout)
{
throw e;
}
fileIORetryTimer.SleepUntilNextRetry();
}
}
那么,如何避免在整个班级中为每个文件IO操作复制大部分代码?我的解决方案是在类中使用匿名委托块和单个方法来执行传递给它的委托块。这让我可以用其他方法做这样的事情:
this.RetryFileIO( delegate()
{
// some code block
} );
我有点喜欢这个,但它还有很多不足之处。我想听听其他人如何解决这类问题。
答案 0 :(得分:13)
这看起来是一个了解面向方面编程的绝佳机会。这是一篇关于AOP in .NET的好文章。一般的想法是,您将跨职能问题(即重试x小时)提取到一个单独的类中,然后您将注释需要以这种方式修改其行为的任何方法。这是它的外观(在Int32上有一个很好的扩展方法)
[RetryFor( 10.Hours() )]
public void DeleteArchive()
{
//.. code to just delete the archive
}
答案 1 :(得分:4)
只是想知道,你觉得你的方法有什么期望?您可以用..命名替换匿名委托?代表,像
public delegate void IoOperation(params string[] parameters);
public void FileDeleteOperation(params string[] fileName)
{
File.Delete(fileName[0]);
}
public void FileCopyOperation(params string[] fileNames)
{
File.Copy(fileNames[0], fileNames[1]);
}
public void RetryFileIO(IoOperation operation, params string[] parameters)
{
RetryTimer fileIORetryTimer = new RetryTimer(TimeSpan.FromHours(10));
bool success = false;
while (!success)
{
try
{
operation(parameters);
success = true;
}
catch (IOException e)
{
if (fileIORetryTimer.HasExceededRetryTimeout)
{
throw;
}
fileIORetryTimer.SleepUntilNextRetry();
}
}
}
public void Foo()
{
this.RetryFileIO(FileDeleteOperation, "L:\file.to.delete" );
this.RetryFileIO(FileCopyOperation, "L:\file.to.copy.source", "L:\file.to.copy.destination" );
}
答案 2 :(得分:2)
您还可以使用更多OO方法:
这样做的好处是可以命名您执行的每种操作,并为您提供命令模式 - 操作已表示为对象。
答案 3 :(得分:2)
这是我最近做的。它可能在其他地方做得更好,但看起来非常干净且可重复使用。
我有一个实用程序方法,如下所示:
public delegate void WorkMethod();
static public void DoAndRetry(WorkMethod wm, int maxRetries)
{
int curRetries = 0;
do
{
try
{
wm.Invoke();
return;
}
catch (Exception e)
{
curRetries++;
if (curRetries > maxRetries)
{
throw new Exception("Maximum retries reached", e);
}
}
} while (true);
}
然后在我的应用程序中,我使用c#的Lamda表达式语法来保持整洁:
Utility.DoAndRetry( () => ie.GoTo(url), 5);
这会调用我的方法并重试最多5次。在第五次尝试时,原始异常会在重试异常中重新抛出。