Out team is at the cusp of a new project.
One of the components at the boundary of the system is the component which interacts with a printer through an external COM component (referenced as a usual dll).
The COM component returns integer codes if there were an error whilst command execution.
Lets consider a concrete code.
public class OperationException:Exception {
public int ErrorCode { get; private set; }
public string ErrorDescription { get; private set; }
public OperationException(int errorCode, string errorDescription) {
ErrorCode = errorCode;
ErrorDescription = errorDescription;
}
}
//The exception throwing way
public class Provider1:IFrProvider {
private readonly IDrvFR48 driver;
public string GetSerialNumber() {
//must read status before get SerialNumber
int resultCode = driver.ReadEcrStatus();
if (resultCode != 0) {
throw new OperationException(resultCode, driver.ErrorDescription);
}
return driver.SerialNumber;
}
}
//The way of out parameters returning.
public class Provider2 : IFrProvider
{
private readonly IDrvFR48 driver;
public string GetSerialNumber(out Result result) {
//must read status before get SerialNumber
int resultCode = driver.ReadEcrStatus();
if (resultCode != 0) {
result = new Result(resultCode, driver.ErrorDescription);
return null;
}
result = new Result(0, null);
return driver.SerialNumber;
}
}
//The way of LastResult property setting.
public class Provider3 : IFrProvider
{
private readonly IDrvFR48 driver;
public Result LastResult {
get {
return new Result(driver.ErrorCode, driver.ErrorDescription);
}
}
public string GetSerialNumber() {
//must read status before get SerialNumber
if (driver.GetECRStatus() == 0)
return driver.SerialNumber;
return null;
}
}
public class Result {
public int ResultCode { get; private set; }
public string Description { get; private set; }
public Result(int resultCode, string description) {
ResultCode = resultCode;
Description = description;
}
}
public class Caller {
public void CallProvider1() {
var provider = new Provider1();
try {
string serialNumber = provider.GetSerialNumber();
//success flow
}
catch (OperationException e) {
if (e.ErrorCode == 123) {
//handling logic
}
}
}
public void CallProvider2() {
var provider = new Provider2();
Result result;
string serialNumber = provider.GetSerialNumber(out result);
if (result.ResultCode == 123) {
//error handling
}
//success flow
}
public void CallProvider3()
{
var provider = new Provider3();
string serialNumber = provider.GetSerialNumber();
if (provider.LastResult.ResultCode == 123) {
//handling
}
//success flow
}
}
So we have three ways of error handling. The most specific thing in all this stuff is that each operation can fail, because it depends on a device.
Our team think of using out parameters, because of unhandled exceptions propagation fear. The other reason, connected with the fear, is that we will have to cover all calls by try\catch blocks to stop to be afraid, but such a code will be pretty ugly.
What pros and cons do you see and what could you advice?
Best Answer
The fear with exception can be eased by learning proper exception handling programming style (and also general error handling techniques).
Proper error handling (applicable to all mechanisms, and particular applicable to exception handling) requires careful reasoning as to the consequences and interpretation of each failed operation. Such reasoning entail:
This reasoning can be extended to any programming black-boxes such as a third-party library.
Code example.
To ensure that every successful call to
QueryStatusStart
is followed up with a call toQueryStatusFinish
with other calls in between:In general programming, status cleanup methods (such as the
QueryStatusFinish
method above) should not throw exception (because throwing an exception in a catch{} or finally{} block will indeed cause the uncaught exception problem you feared.However, when dealing with a physical device, there is a need to interpret what a failed cleanup (restoration) means:
In either case, the program will not be able to continue its normal operation. The correct way to handle this situation varies from case to case.