Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Reducing duplicate error handling code in C#?

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.

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.

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.

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:

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.

like image 747
Wedge Avatar asked Aug 04 '08 19:08

Wedge


People also ask

How do you reduce code duplication?

To avoid the problem of duplicated bugs, never reuse code by copying and pasting existing code fragments. Instead, put it in a method if it is not already in one, so that you can call it the second time that you need it.

How do I refactor a repeated code?

If the duplicate code is inside a constructor, use Pull Up Constructor Body. If the duplicate code is similar but not completely identical, use Form Template Method. If two methods do the same thing but use different algorithms, select the best algorithm and apply Substitute Algorithm.

How do you avoid code duplication in C++?

The conventional approach to reduce this kind of code duplication is to move the common code to a member function, which can be called from all the constructors. Usually, that member function is called init.

How do I find duplication codes?

Duplicate code can be hard to find, especially in a large project. But PMD's Copy/Paste Detector (CPD) can find it for you! CPD works with Java, JSP, C/C++, C#, Go, Kotlin, Ruby, Swift and many more languages. It can be used via command-line, or via an Ant task.


2 Answers

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)

[RetryFor( 10.Hours() )] public void DeleteArchive() {   //.. code to just delete the archive } 
like image 69
Mike Minutillo Avatar answered Oct 17 '22 20:10

Mike Minutillo


Just wondering, what do you feel your method leaves to be desired? You could replace the anonymous delegate with a.. named? delegate, something like

    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" );     } 
like image 42
Chris Marasti-Georg Avatar answered Oct 17 '22 21:10

Chris Marasti-Georg