Exceptions vs Result Codes for a socket client class
I have a class that encapsulates tcp socket communications with a server. For each command message sent to the server, the server will send back a response message that invariably contains a response code (OK, Fail). Using my class, each command can be executed either sync or async.
There are basically two types of exceptions that can take place: A "fault" that is caused by a disconnect or some other non-recoverable error and an unexpected exception like "send buffer is full". In the event of a fault, no command can continue or try again or anything until the connection is re-established. In the event of a fail response or even an exception, the command can be tried again...
So, right now my sync command methods return an enum that can have the following values: OK, Fail, Fault. If an exception occurs, it is simply raised to the calling thread (in a sync command). For async commands, the Result property enum value can contain an extra value: OK, Fail, Fault or Exception and the callback can access the actual exception object via the command object's Exception property.
What do you think about this strategy? I am tempted to not raise exceptions at all for sync commands and just log the exception internally and return the 4th enum value instead because that's all I'll really do with exceptions in any given case anyway... Or, should I not be using result codes at all and just raise exceptions in all cases, even faults?
I think your strategy is basically sound.
Keep in mind that the purpose of Exceptions is to deal with exceptional conditions. The closer to the source of the problem, the better.
In your case, it appears that your strategy is something like "It didn't work right now. Let's retry". I don't see a reason to really raise exceptions.
If dealing with a closed socket was something that required a totally different flow in your code, then maybe exceptions would make sense. From your description, that's not really the case.
My philosophy on Exceptions is that they should be for exceptional conditions that you can't really deal with. A closed socket? Hmm...how many times does the internet go down at my house...
My preference is you throw an exception any time your method does not successfully complete its mission. So if I, the caller, call yourObject.UploadFile(), I will assume the file was uploaded successfully when the call returns. If it fails for any reason, I expect your object will throw an exception. If you want to distinguish between commands I can retry and commands I shouldn't retry, put that information in the exception and I can decide how to react accordingly.
When calling yourObject.BeginAsyncUploadFile(), I'd expect the same behavior except that I'd need to wait to on the IAsyncResult or equivalent object to find out whether the file upload succeeded or not and then check an Exception/Error property if it didn't.
Result codes and exceptions can both work fine. It is a matter of personal taste (and the taste of the others on your team). Exceptions have some advantages, especially in more complex settings, but in your setting it sounds simple enough that return codes should work okay.
Some people will foam at the mouth and insist on exceptions, but on my project people like the simplicity of return codes, making them the better choice overall.
This is a rather interesting question. As such, there's probably no '100% correct' answer, and mostly it depends on how do you think the code using your function should be structured.
The way I see it is you use exceptions only when you want to provide the code calling your function with a way to escape gracefully from a 'disastrous' situation. So, in my code I normally throw an exception when something really, really horrible happens, and the caller needs to know.
Now, if what you have is a normal, and expected, situation you should probably return an error value. That way the code knows it needs to 'try harder', but it won't be compromised by what happened.
In your case, for example, you could treat the timeouts as something expected, and hence return an error code, and more severe problems (like a full send buffer) where the calling code needs to perform some extra actions to go back to 'normal' as an exception.
But then, beauty is in the eye of the beholder, and some people will tell you to only use exceptions, others (mostly C programmers) to only use return codes. Just remember, exceptions should always be exceptional. :)