LA.NET [EN]

Sep 08

Exceptions in .NET– part III

Posted in .NET Basics C#      Comments Off on Exceptions in .NET– part III

As I’ve said before, you’re supposed to throw an exception when a method cannot complete its task in the way it’s supposed to do it. Before throwing an exception, you should consider two things:

  • which type of exception should you throw?
  • What’s the message that  you’re going to pass into the Exception constructor?

The first step is really important: you should choose an exception type which is meaningful. This is an important decision because you need to consider what will happen when you throw that exception. You should start by looking at the predefined exceptions introduced by .NET. If none of those brings the semantics you’re looking for, then you should probably create a new one. In this case, I’d say that you’re better off by reusing Exception as the base type of the new exception (in fact, if you need to create several exceptions for a specific application, then it’s best to make the hierarchy shallow and wide, ie, with as few base classes as possible).

After choosing the exception type, you should also give some thought about the message you’re passing in when you instantiate that exception. As I’ve said before, this should be a technical message, so don’t be afraid to get into details. Whenever an exception isn’t caught, it will end up as an unhandled exception. In practice, this means that it will probably end up in the event log and, when that happens, this message should be a good starting point for us to know how to handle the current problem. Since this message shouldn’t really be shown to the user, then you probably don’t need to worry about localizing it.

Having said this, I must confess that there is a place where I don’t follow these guidelines: whenever I need to develop WCF services, I tend to catch eventual expected exceptions and transform them into new exceptions, with user friendly messages tackled into its, which are propagated into the client (WPF or Silverlight).

If you decide to create a new Exception derived class, then you can expect to have some tedious work ahead of you. For instance, suppose you’ve got a method which changes the value of a property. Furthermore, suppose you want to improve the semantics of your code by generating an IncorrectNameException whenever the string that is passed in is not limited to 30 characters…In that case, the first thing we need to do is build a new Exception derived class:

[Serializable]
public sealed class IncorrectNameException:Exception, ISerializable {
    private readonly String _incorrectName;
    public IncorrectNameException(String incorrectName = null,
        String message = null,
        Exception innerException = null)
    :base(message, innerException) {
        _incorrectName = incorrectName;
    }
    private const String serializedFieldName = "incorrectName";
    [SecurityPermission(SecurityAction.LinkDemand,
        Flags = SecurityPermissionFlag.SerializationFormatter)]
    private IncorrectNameException( SerializationInfo info,
        StreamingContext context )
        : base( info, context ) {
        _incorrectName = ( String )info.GetValue(
                    serializedFieldName, typeof(String) );
    }

    public override void GetObjectData(SerializationInfo info,
        StreamingContext context) {
        info.AddValue( serializedFieldName, _incorrectName );
        base.GetObjectData(info, context);
    }
    public override string Message {
        get {
            return String.IsNullOrEmpty( _incorrectName )
                       ? base.Message
                       : base.Message + "–>" + _incorrectName;
        }
    }
    public override bool Equals(object obj) {
        var aux = obj as IncorrectNameException;
        if(aux == null) {
            return false;
        }
        return _incorrectName == aux._incorrectName &&
                                 base.Equals( obj );
    }
    public override int GetHashCode() {
        return base.GetHashCode();
    }
    public String IncorrectName { get { return _incorrectName; } }
}

The first interesting point is that we should always make the new exception serializable. Besides applying the Serializable attribute, I’ve also implemented the interface and overrode the GetObjectData inherited from the base Exception class (the serialization part is simple and it will only save/restore the value of the _incorrectName field). In order to personalize the message, I’ve also opted for overriding the Message property so that it appends the name of the field to the message that is passed in the constructor. Btw, and since I’ve mentioned the constructor, notice how I’m taking advantage of optional parameters (instead of writing three constructors, I’m writing just one! – the second one is only there for deserialization purposes). I’ve also overridden the Equals method (and the GetHashCode) to ensure that the comparison between two instances of this type is perfomed correctly.

The code isn’t really complicated, but, as I’ve said before, is rather tedious. If you want, you can also use generics to create a new generic type which you can reuse for all your exceptions types (in this case, it’s clearly a good idea to transform the String parameter into a generic type parameter!).

After defining the class, we’re ready to throw our custom exceptions:

private const Int32 _maxNameSize = 30;
static void ChangeName(String name) {
    if(String.IsNullOrEmpty( name ) ||
        name.Length > _maxNameSize) {
        throw new IncorrectNameException( name, "The passed in name doesn't comply with the maximum name size rule" );
    }
    //rest of the code goes here
}
private static void Main( string[] args ) {
    try {
        ChangeName( "This is a really really huge name so it will probably trigger the exception" );
    }
    catch(IncorrectNameException ex) {
        Console.WriteLine( ex.Message);
    }
}

I couldn’t end this post without mentioning that I’d wouldn’t really create this new exception type in a “real world” application (at least, not in my domain code). The reason is simple: .NET already offers an ArgumentException class which is more than adequate for this kind of error. In fact, I would leave these verifications for the code contract framework, which does a very good job in verifying pre and post conditions and invariants!

And that’s it for now. Stay tuned for more.