Exception handling could turn to costly operation in your code if you don’t pay much attention to it.
I feel in real life, this is like driving a non-automatic (with gear) car efficiently to get good mileage and that is possible only if you use the gear shift and clutch properly or else your car will turn to fuel drinker :) . Similarly if you don’t take care of computing and memory efficiently then your program too will turn to bad in performance and exception handling plays a key role here so lets discuss about it.
- Use Catch only if needed as it is costly operation, if possible use if instead.
i.e. Divide by zero exception. why not do a check for zero before divide operation if possible.
try
{
var result = myVal/someParam;
}
catch(DivideByZeroException ex)
{
//do what you want
}above code can be refactored like below to avoid the costly catch operation.if(someParam != 0)
{
var result = myVal/someParam;
}
else
{
//do what you wanted to do in the catch block
}
similar situation can arises with null values too and costly exception can be avoided using null checks.
2. You must ignore catching exception if you are not handling it. Though in some catch it may be necessary when you want to ignore the exception as fallback mechanism.
try
{
//code
}
catch(Exception)
{
}
return something;
In above example it is certainly not a good practice unless there is a unavoidable circumstance to do so. In this case if possible to avoid the catch with check please do so, or even if there is possibility where caller of this handling the exception then leave it to them instead of catching here.
3. Like above point, also avoid throw if not necessary. Consider below examples where certainly throw is not necessary.
try
{
//code
}
catch(Exception ex)
{
throw ex;
}
return something;ORtry
{
//code
}
catch(Exception)
{
throw;
}
return something;
In above example, programmer is not caring or respecting the exception but still handling it throw it back to the caller, why to do so when the catch is avoided here. In any case caller of this block is expecting an exception as code says so let it go as it is and remove the try catch completely.
4. In case if and there is a need, where we need to process the exception in above example (like logging or handling fallback mechanism etc) then throw an exception then use throw only instead of throw ex to save the stack trace info.
//Bad Code
try
{
//code
}
catch(Exception ex)
{
//do something
throw ex;
}
return something;//better Code
try
{
//code
}
catch(Exception ex)
{
//do something
throw;
}
return something;
Above code scenario can be refactored well to avoid throw and rely on return.
5. Avoid dirty Nested try catch. lets see the example.
//Bad code
try
{
//some code
try //inner try catch 1
{
//some code
}
catch(Exception ex)
{
throw ex;
}
try //inner try catch 2
{
//some code
}
catch(Exception ex)
{
throw;
}
}
catch(Exception ex)
{
//code to handle the exception
}
Above example can not be justified even though inner try catch block is the demand of program nature. This example code must be refactored as.
{
method1();
method2();
}private void method1()
{
try //inner try catch 1
{
//some code
}
catch(Exception ex)
{
//Handle exception and do not throw back to caller.
}
}private void method2()
{
try //inner try catch 1
{
//some code
}
catch(Exception ex)
{
//Handle exception and do not throw back to caller.
}
}
OR
try
{
method1();
method2();
}
catch(NullException ex)
{
//Handle exception. If needed catch can be classified well based on the exception type behavior of method1 and method 2.
}
catch(Exception ex)
{
//Handle exception
}private void method1()
{
//code
}private void method2()
{
//code
}
Any way the final goal of the code refactoring should be “avoiding multiple catch blocks”.
6. Use the finally block to respect the caller. This is very powerful features which can and must be used to cleanup your mess instead making it tough for the caller.
{
var result = method1();
method2();
}private int method1()
{
int result;
try //inner try catch 1
{
//some code
}
catch(Exception ex)
{
//Handle exception and do not throw back to caller.
}
finally
{
result = 0;
}
return result;
}private void method2()
{
try //inner try catch 1
{
//some code
}
catch(Exception ex)
{
//Handle exception and do not throw back to caller.
}
finally
{
//clean your mess-ups, like closing the open connection, fallback mechanism etc.
}
}
7. Use Custom Exceptions. This is very important feature to avoid letting caller know the internal root cause failure for code security as the stack trace has lot of information.
//Bad Practice
public Result PerformAction()
{
try
{
}
catch(Exception ex)
{
throw;
}
}//Good Practice
public Result PerformAction()
{
try
{
}
catch(Exception ex)
{
return new CustomException("An internal error occured");
}
}public class CustomException : Exception
{
}
8. Last but not least, Never forget to handle any uncaught exception in your Gateway/Entry/Main method. i.e.
if you are in .Net Core, use the middleware or filters to handle uncaught exception generically. Follow the article “Middleware and Filters power in ASP.NET Core” to know more about Middlewares and Filters.
Hope you enjoyed reading. Thank You.
No comments:
Post a Comment