tags:

views:

611

answers:

5

(Long time reader of SO, first time asking a q.

I'm quite new to C# having been in the PHP/Ruby/Python world for many years so I apologise if this is a dopey question.)

I'm doing some maintenance on an old C# app that is crashing whenever SmtpClient.Send() fails. From the little I've gleaned from the MSDN, I can see the obvious way to fix this, but my question is also concerned with the more general case.

As per the MSDN:

  try {
          client.Send(message);
  }  
  catch (Exception ex) {
          Console.WriteLine("Exception caught in CreateTestMessage2(): {0}", 
                ex.ToString() );
  }

This all makes sense to me, but I've also always thought that whenever you can prevent the possibility of the error you do so. Is there anything you can (and should?) do here to reduce the possibility of the Send() throwing exceptions?

I'm assuming there are situations where it's impossible to prevent the possibility of an exception, so you have to handle it, but are there any general style guide or rules that people use to guide them?

Again, sorry if this is a crap question. I tried searching SO and Google as much as I could.

EDIT: I just found this question Best practices for exception management in java or C which may well go someway to answering my question.

EDIT2: Thanks for the prompt feedback, incredibly fast. I've been thinking about the problem a bit more and perhaps this could further refine what I'm asking.

Is it correct to say that certain exceptions, like SmtpException, really can't be avoided? Is it even more correct to say that it is the correct style to use exceptions like SmtpException to tell you that something went wrong with the send and you just handle it however you'd like?

I feel like I'm sounding a bit dim in this question, but I'm asking because anything I can learn is good for my confidence.

+1  A: 

By reading MSDN you can see the list of Exceptions thrown by the .Send() method and abbreviated reasons why they would be thrown. Checking for these and handling them prior to calling Send() can help avoid them--but the documentation doesn't necessarily cover all possible exceptions or reasons why they could occur.

You're on the right path though; and you're right that you want to avoid creating exceptions if possible. If for no other reason they are fairly expensive for the runtime to create and handle--and if they go unhandled even a simple exception can cause the whole app to crash.

STW
+1  A: 

The exceptions are not very bad per se. They are part of defensive programming strategy. You will get valuable knowledge from exception caught regarding the causes of crashing of Send. Which you will be able to use to fix the problems.

A: 

It's completely ok to throw an exception when an exceptional error occurs, so there's nothing wrong with your code. If the SMTP server is down or the email address is invalid or some other mistake is made, you want it to throw rather than fail silently.

Whether you want a try block immediately surrounding the Send depends on whether there's anything for you to do here. Otherwise, you might as well let it bubble up to the to-most try block.

Steven Sudit
+2  A: 

When catching exceptions, be as specific as possible if you know what kinds of exceptions to expect from the invoked method. This will allow, for example, errors such as OutOfMemoryException to continue to bubble up the stack where it should go unhandled and then your app will fail fast (which is probably a good thing because your system is now in an unknown state and you should not carry on).

There are different schools of thought on this, however; in some instances (say you're an NT service) you want high availability of your app, and crashing in production because you get NullPointerException on some unforeseen code path may not be desirable as long as that exception gets logged and you have the ability to then issue a QFE (not to mention revise your test regime). If you're a console or forms app, then recovery is a different story since the exception could be surfaced to the user and they can interactively decide what the appropriate action is.

My general advice: catch specific exceptions as close to the source as possible, let the rest bubble up the stack and have it logged at a point where you have enough context to later on try and repro the exception. Be wary that catching then re-throwing is expensive; a scenario in your example where you might want to do this would be if, say, the SmtpException was a connection timeout (I'm making this up), then one strategy might be exponential back off and retry up to n times since the mail server might be down, then finally give up and rethrow if you don't succeed.

The really short answer is: It All Depends.

Really nice overview, thanks.
SimonF
+4  A: 

This is a good example of what I call an "exogenous" exception situation.

http://blogs.msdn.com/ericlippert/archive/2008/09/10/vexing-exceptions.aspx

Consider an analogous case -- opening a file. You can reduce the chance of getting an exception by:

  • checking whether the file exists
  • checking whether the user has permission to open the file
  • checking whether another application has locked the file
  • blah
  • blah blah
  • blah blah blah

You can do all that and STILL get an exception when you open the file. Because between doing all those checks and you trying to open the file, something might have changed. Another process might have changed the permissions, or locked the file, or the user removed the CD-ROM from the drive, or whatever. Whether the action succeeds or not is based on an exogenous, real-world condition that you cannot test for in any kind of guaranteed way.

Exogenous exceptions are a pain to deal with, because you HAVE to handle them even if you do lots of work to eliminate them.

Sending mail is the same way. You can check all kinds of things to try to eliminate the exception, which probably will work 99% of the time, but you still cannot GUARANTEE that between all your checks and the actual attempt to send mail, that someone didn't unplug the router at exactly the wrong moment. You're just going to have to suck it up and handle the exceptions.

Eric Lippert
Haha wow, I really should learn to come back and check my old questions. I never expected to get an answer from Eric himself! Thanks for the answer. I've since done a hell of a lot more work in .NET (currently wrestling with EF4, MVC2 and WCF, yum yum) and looking back at this question made me smile.
SimonF