减少 C# 中重复的错误处理代码?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1535/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Reducing duplicate error handling code in C#?
提问by Wedge
I've never been completely happy with the way exception handling works, there's a lot exceptions and try/catch brings to the table (stack unwinding, etc.), but it seems to break a lot of the OO model in the process.
我从来没有对异常处理的工作方式感到完全满意,有很多异常和 try/catch 带来了表(堆栈展开等),但它似乎在这个过程中破坏了很多 OO 模型。
Anyway, here's the problem:
无论如何,问题来了:
Let's say you have some class which wraps or includes networked file IO operations (e.g. reading and writing to some file at some particular UNC path somewhere). For various reasons you don't want those IO operations to fail, so if you detect that they fail you retry them and you keep retrying them until they succeed or you reach a timeout. I already have a convenient RetryTimer class which I can instantiate and use to sleep the current thread between retries and determine when the timeout period has elapsed, etc.
假设您有一些类包装或包含网络文件 IO 操作(例如,读取和写入某个特定 UNC 路径中的某个文件)。出于各种原因,您不希望这些 IO 操作失败,因此,如果您检测到它们失败,请重试它们,并继续重试,直到它们成功或超时。我已经有一个方便的 RetryTimer 类,我可以实例化并使用它在重试之间休眠当前线程并确定超时期限何时过去等。
The problem is that you have a bunch of IO operations in several methods of this class, and you need to wrap each of them in try-catch / retry logic.
问题是你在这个类的几个方法中有一堆IO操作,你需要把它们每个都包装在try-catch/retry逻辑中。
Here's an example code snippet:
这是一个示例代码片段:
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();
}
}
So, how do you avoid duplicating most of this code for every file IO operation throughout the class? My solution was to use anonymous delegate blocks and a single method in the class which executed the delegate block passed to it. This allowed me to do things like this in other methods:
那么,如何避免在整个类中为每个文件 IO 操作复制大部分代码?我的解决方案是在类中使用匿名委托块和一个方法来执行传递给它的委托块。这让我可以用其他方法做这样的事情:
this.RetryFileIO( delegate()
{
// some code block
} );
I like this somewhat, but it leaves a lot to be desired. I'd like to hear how other people would solve this sort of problem.
我有点喜欢这个,但它还有很多不足之处。我想听听其他人如何解决此类问题。
采纳答案by Mike Minutillo
This looks like an excellent opportunity to have a look at Aspect Oriented Programming. Here is a good article on AOP in .NET. The general idea is that you'd extract the cross-functional concern (i.e. Retry for x hours) into a separate class and then you'd annotate any methods that need to modify their behaviour in that way. Here's how it might look (with a nice extension method on Int32)
这看起来是一个了解面向方面编程的绝佳机会。这是一篇关于.NET 中 AOP的好文章。一般的想法是,您将跨功能关注点(即重试 x 小时)提取到一个单独的类中,然后您将注释任何需要以这种方式修改其行为的方法。这是它的外观(在 Int32 上有一个很好的扩展方法)
[RetryFor( 10.Hours() )]
public void DeleteArchive()
{
//.. code to just delete the archive
}
回答by Chris Marasti-Georg
Just wondering, what do you feel your method leaves to be desired? You could replace the anonymous delegate with a.. named? delegate, something like
只是想知道,您觉得您的方法有什么不足之处?你可以用一个..named 替换匿名委托?委托,类似
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" );
}
回答by Andrew Peters
You could also use a more OO approach:
您还可以使用更面向对象的方法:
- Create a base class that does the error handling and calls an abstract method to perform the concrete work. (Template Method pattern)
- Create concrete classes for each operation.
- 创建一个执行错误处理并调用抽象方法来执行具体工作的基类。(模板方法模式)
- 为每个操作创建具体的类。
This has the advantage of naming each type of operation you perform and gives you a Command pattern - operations have been represented as objects.
这具有命名您执行的每种类型的操作的优点,并为您提供命令模式 - 操作已表示为对象。
回答by Andrej Kyselica
Here's what I did recently. It has probably been done elsewhere better, but it seems pretty clean and reusable.
这是我最近所做的。它可能在其他地方做得更好,但它看起来非常干净且可重复使用。
I have a utility method that looks like this:
我有一个看起来像这样的实用方法:
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);
}
Then in my application, I use c#'s Lamda expression syntax to keep things tidy:
然后在我的应用程序中,我使用 c# 的 Lamda 表达式语法来保持整洁:
Utility.DoAndRetry( () => ie.GoTo(url), 5);
This calls my method and retries up to 5 times. At the fifth attempt, the original exception is rethrown inside of a retry exception.
这会调用我的方法并重试最多 5 次。在第五次尝试时,原始异常在重试异常中被重新抛出。