Exceptions vs ErrorCodes when working with devices

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 trycatch 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?

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:

  1. After encountering an error, which line of code will it execute next?
  2. After encountering an error, what does it say about the current state of the device?
    • Can the device resume normal operation?
    • Does the device need a reset?
    • Does the human user need to be notified?
  3. After encountering an error, is the program state still consistent?
    • Should the program do some cleanup to restore the program state’s consistency?

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 to QueryStatusFinish with other calls in between:

bool queryStatusStarted = false;
try
{
    // throws exception if fails
    device.QueryStatusStart(); 

    // this line is not executed unless the previous line succeeded.
    queryStatusStarted = true; 

    // might throw exception
    device.MakeMoreQueryStatusCalls(); 
}
finally 
{
    // Whether or note MakeMoreQueryStatusCalls() throws an exception,
    // we will reach here. Hence we can restore the device to a 
    // known state.

    if (queryStatusStarted)
    {     
        device.QueryStatusFinish();  
    } 
}

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:

  1. Device has disconnected
  2. Device has malfunctioned and require human intervention (such as a power reset)

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.

I don’t see that using error codes is going to gain you anything over exceptions. It’s just as easy to miss checking an error code, and then what?

Also, consider the following example:

public void CallProvider2() {
    var provider = new Provider2();

    Result result;
    string serialNumber = provider.GetSerialNumber(out result);
    if (result.ResultCode != 0) {
        //error handling
        switch(result.ResultCode)
        { 
            case 123:
            break;
            //more cases
            default:
                //what now?
            break;
        }
    }
    //success flow
}

The above is how you might handle errors, and the “default” case would catch unchecked error codes and would be the equivalent of an unhandled exception. It would be worthwhile to ask what should be done if that case is ever reached. You can’t continue, but you’ve got to safely exit that method or what? Are you just going to return the Result object all the way up the stack and check if ResultCode !=0 everywhere? Might as well stick to exceptions.

Other benefits of exceptions to consider:

1) You get a stack trace automatically, which can be enormously useful in debugging.

2) If you need to handle an error further up the call stack, exceptions propagate upwards automatically. You can also handle and rethrow them. In the ResultCode examples you’d have to write additional code to pass them around if needed.

3) .NET runtime projects have some variation of an UnhandledException event you can subscribe to and do some centralized error logging. Even if your app goes down, you can still get some information about why. The plumbing to do that is already built in, you just have to attach to it.

4) An app crash from an unhandled exception, while undesirable, is USEFUL. No one can mistake that something went wrong.

1

Very debatable question, that depends on many conditions of your project, workflow, whether your code will be used by other developers, etc.

Yes, the common .Net practice is to throw exceptions instead of returning an error code. Very good comment about it here.

Error codes are old skool, you needed them back in the bad old days of COM and C programming, environments that didn’t support exceptions. Nowadays, you use exceptions to notify client code or the user about problems. Exceptions in .NET are self-descriptive, they have a type, a message and diagnostics.

Thought, you need to have in mind other aspect – if the code of the error itself is also self-descriptive (like http codes, 404, 503, etc), then you can return the code. And the .Net framework actually returns such code with HttpWebResponse.StatusCode (though still wrapped with enum). But do it if you know for sure (he-he) that these codes won’t change in future.

In other cases, what sense to user of your code to know that it was -lets say – error #23452, that was occurred. Besides, it can be changed in future, that causes the user to override his code because of number changed.

1

When designing an API for operations which may fail because of foreseeable reasons, but at unforeseeable times, one should generally allow a means by which callers can indicate whether they are expecting to cope with the semi-foreseeable failures. If the only methods which are available return with error codes rather than exceptions, then every call site will be required to test the return value, even if the only thing it can do with an error code is throw an exception. If the only methods which are available throw exceptions for all failures, even ones a caller might be expecting, then every caller that would be prepared to handle a failure will have to use a try/catch for that purpose.

A common means for exposing methods whose callers may or may not expect failures is to have pairs of methods that fnorble named, e.g., TryFnorble and Fnorble. Suppose Thing1a and Thing1b have both styles of method, while Thing2a only has the first and Thing2b only has the second. Code which is prepared to handle a problem with the first thing but not the second could be written as:

Thing1a.Fnorble();
if (Thing1b.Fnorble())
  Thing1bIsReady = true;
else
  Thing1bIsReady = false;

By contrast, if the code was using Thing2a/Thing2b, it would need to be written as:

if (!Thing2a.Fnorble())
  throw new FnorbleFailureException(...);
try
{
   Thing2b.Fnorble();
   Thing2bIsReady = true;
}
catch (Ex as FnorbleFailureException)
{
   Thing2bIsReady = False;
}

Much less nice.

[Note: I use an if/else and explcitly set/clear the Thing1bIsReady flag, rather than simply assigning the return value directly to the flag, because even though the return type and flag are both bool, I would regard them as having semantically different meanings. I would consider the fact that having the method return true once would cause the flag to be set immediately, and having it return false once would cause the flag to be cleared immediately as an implementation detail.

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa
Thiết kế website Thiết kế website Thiết kế website Cách kháng tài khoản quảng cáo Mua bán Fanpage Facebook Dịch vụ SEO Tổ chức sinh nhật