I have a control circuit that I communicate with via serial port. If the response command doesn't match a certain format I consider it to be an error and was wondering if I should return an error code or throw an exception? For example:
public double GetSensorValue(int sensorNumber)
{
...
string circuitCommand = "GSV,01," + sensorNumber.ToString(); // Get measurement command for specified sensor.
string responseCommand;
string expectedResponseCommand = "GSV,01,1,OK";
string errorResponseCommand = "ER,GSV,01,1,62";
responseCommand = SendReceive(circuitCommand); // Send command to circuit and get response.
if(responseCommand != expectedResponseCommand) // Some type of error...
{
if(responseCommand == errorResponseCommand) // The circuit reported an error...
{
... // How should I handle this? Return an error code (e.g. -99999) or thrown an exception?
}
else // Some unknown error occurred...
{
... // Same question as above "if".
}
}
else // Everything is OK, proceed as normal.
...
}
Thanks!
In almost all cases I would communicate errors via exceptions. I would almost never use "error codes" as such - occasionally it's useful to give success/failure along with a result as in int.TryParse
etc, but I don't think this situation works like that. This sounds like a real error condition which should stop further progress though, so an exception is appropriate.
EDIT: As noted in comments, if the circuit reporting an error really is "expected" and the caller should be able to deal with it and should actively be looking for that situation, then it's reasonable to use a status code.
Unfortunately error handling is one of those things that we haven't really got right in software engineering yet...
Strictly speaking an exception would be used when you're in an 'exceptional' circumstance, i.e. one that you wouldn't expect.
If it were me I'd probably return an error code in the first case (known error response), and throw an exception in the second case (unknown error code)
In your case throwing an exception is better. Because the caller of the method can differentiate a "legitimate" output and an error condition. If you return an error code it is still a numeric value so the caller may misinterpret it as an output from control circuit.
Personally, I like to deal with this situation by defining a response object of some kind. I've arrived at this via experience doing this and weighing what should be considered exceptional. If you're interfacing with some device and the user turns the device off or something, this is an exceptional condition, and you'd throw an exception.
If the particular method call to the device fails or something, that's not really exceptional, but it is an error. I don't want to define a magic number if I'm returning a double, and I don't want to throw an exception. So, I define something like:
class DeviceResult //could also be struct
{
public double ReturnValue { get; set; }
public bool IsValid { get; set; }
public string ErrorMessage { get; set; }
}
The specifics here aren't really important -- you'll adapt it to what clients of your method want. In this example, the client API would be to check for valid and, if so, use the return value -- no magic number and no exception. The paradigm is what is more important. The way I see it, you're using an object oriented language -- so you might as well use objects. :)
Error code.
- Throwing exceptions works slower than returning values.
- You are free to design the method signature, so you might return any type you want. If you had to return some specific type which couldn't be extended in a meaningful way, then you definitely had to throw an exception.
- It is easier to overlook exceptions than error codes when invoking a method. You'll have to document the exceptions which your method is throwing and hope that the other developer will read it.
- The response of GetSensorValue method has different paths. The "happy path" is obviously the expected and the most interesting one. But if you know that the consumers can handle results of less hapy paths, then it makes sense to go with the error code as the "unhappy path" response is more expected.
- Throwing lots of exceptions can cause your debuger to constantly break depending on your debugger exception settings.
- Returning error codes will definitely make your response type more complex, which would require more code to unwrap the value. But is it really hard to read and write "if (response.Status == Statuses.Success) { var value = response.Value; }" instead of try-catch?
When it is a good practice to throw exceptions:
- When you can foresee that your method invocation will fail (checking if you can perform an operation before performing it). The "perform" method could be implemented to return an error code too, but this check-perform methods pair is considered a pattern.
- You can't extend response type in a meaningful way, but you need to signal a response in a state which was not foreseen by the method's contract.
- Similar to previous, you have good reasons to return only "happy path" result.
I would recommend using exceptions in most cases due to a few simple facts:
- Exceptions are global, all C#-developers knows what an exception is (and hopefully) how to use it for debugging.
- Exceptions breaks out of all calling methods to the nearest "try catch".
If I have several layers and the innermost layer throws an exception then I can handle that exception in whatever layer I wish (normally the outermost or second outermost layer).
- Most project types have some kind of overridable method or event that can be overridden/subscribed to in order to implement simple global exceptionhandling code. This makes it simple to format error messages, perform logging, send mails and pray to the gods (or whatever you guys do when you handle exceptions ;) ).
There are a few things to consider though:
- It's not free to throw an exception. If your code needs to be 100% optimized then throwing exceptions might be something you wish to avoid.
- Exceptions has a lot of information you might not need. Do you really need a stacktrace if all you want to do is to show a user a simple error message?
But as a general rule, I'd say: stick to Exceptions, it's simple, informative, debuggable and easy to handle even though it might come at a slight performance cost