views:

122

answers:

3

I'm coding a simple little class with a single method send an email. My goal is to implement it in a legacy Visual Basic 6 project, exposing it as a COM object via the COM Interop facility.

There's a detail I'm finding difficult to resolve, that being how granular should I be at validating parameters. On that light, a thing I'm really not happy about, and which is not a detail at all, is the way I'm actually handling exceptions:

public class MyMailerClass
{
    #region Creation
    public void SendMail(string from, string subject, string to, string body)
    {
        if (this.IsValidMessage(from, subject, to, body)) // CS1501
        {
            MailMessage msg = new MailMessage();
            msg.IsBodyHtml = true;
            msg.From = new MailAddress(from);
            msg.To.Add(to);
            msg.Subject = subject;
            msg.Body = body;
            SmtpClient srv = new SmtpClient("SOME-SMTP-HOST.COM");
            srv.Send(msg);
        }
        else
        {
            throw new ApplicationException("Invalid message format.");
        }
    }
    #endregion Creation

    #region Validation
    private bool IsValidMessage(string from, string subject, string to, string body)
    {
        Regex chk = new Regex(@"(\w+@[a-zA-Z_]+?\.[a-zA-Z]{2,6})");
        if (!chk.IsMatch(from))
        {
            return false;
        }
        if (!chk.IsMatch(to))
        {
            return false;
        }
        if (!string.IsNullOrEmpty(subject))
        {
            return false;
        }
        if (!string.IsNullOrEmpty(body))
        {
            return false;
        }
        else
        {
            return true;
        }
    }
    #endregion Validation
}

Any suggestion will be much appreciated, so thanks much in advance for all your comments!

Note: Would it be convenient to implement Enterprise Library's Validation Application Block on this particular case?

+3  A: 

Having two throw statements in a row makes no sence - only the first one will be executed and then control will be passed to the exception handler and never to the second throw.

In my opinion it is more than enough to just say smth like "Sender e-mail is invalid." An e-mail is quite simple and short and so the user will be able to resolve this without any additional guidance.

I also think it would be better to first check all the passed in values and only then start work. What's the point in partially doing work if you can then encounter an invalid parameter value and throw an exception and never complete this work. Try to indicate errors as early as you can - at the very beginning if possible.

sharptooth
So you mean like checking, perhaps in ternary way, the validity of every parameter at the very beginning of the method, sort of carrying a boolean flag maybe in the form of "isMsgOk = true" to the end and then doing all the composing there?
Nano Taboada
No, why use a flag when you have exceptions? You can check each parameter and once you found a first invalid value just throw an exception.
sharptooth
+1  A: 

And:

Use

string.IsNullOrEmpty(subject)

rather than

subject == null

for checking if your strings are empty.

Jason Williams
Thanks for that snippet Jason! I'm including it.
Nano Taboada
+6  A: 

Consider the contract that you are imposing upon the callers of SendMail. They are required to pass you a "valid email address". Who decides what is valid? SendMail does. Basically your method is "high maintenance" -- it wants things exactly the way it likes, and the only way to tell whether what you're going to give it will be satisfactory is to try and hope for the best.

Don't write high-maintenance methods without giving the caller a chance to know how to satisfy it, or at least have a way to avoid the exception. Extract the validation logic to an "IsValidAddress" method that returns a Boolean. Then have your SendMail method call IsValidAddress and throw if it is invalid.

You get several nice effects from this change:

(1) Increased separation of concerns. SendMail's job is to make the email mechanism work, not to pass judgment on whether an email address is valid. Isolate that policy decision to code that specializes in verification.

(2) Address validation is a useful tool in of itself; there are lots of times when you want to know whether an address is well-formed without sending mail to it.

(3) You can update and improve your validation logic easily because it is all in one sensible place.

(4) Callers have a way that they can guarantee that no exception will be thrown. If a caller cannot call a method without guaranteeing that the arguments are valid, then they have to catch the exception. Ideally you should never make a caller have to handle an exception to make their code correct; there should be a way they can write correct code that never throws, even if the data they've been handed is bad.

Here are a couple of articles I've written on this subject that you might find helpful:

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

High-maintenance methods: http://blogs.msdn.com/ericlippert/archive/2008/09/08/high-maintenance.aspx

Eric Lippert
Eric, I want to thank you a lot for such elaborated and solid advice!
Nano Taboada