Saturday, August 20, 2011

Careful with the C# "as" operator

It's very common to use the as operator instead of it's "ugly cousin", the explicit cast. The reason behind this is that as appears to be more clear to the reader, demanding less cognitive effort from the mind.

1. Using as


1:    Panel getSomeElem()  
2:    {    
3:      var some_ui_elem = this.Children[0]; // Assume at least one element     
4:      return some_ui_elem as Panel;    // Using “as”  
5:    }  
6:    void AnotherMethod()  
7:    {  
8:      var panel = getSomeElem();           
9:      panel.Height += 100;         // A  ...  
10:    }  

2. Using cast


1:    Panel getSomeElem()  
2:    {   
3:      var some_ui_elem = this.Children[0];  // Assume at least one element     
4:      return (Panel)some_ui_elem;           // Using a explicit cast. B  
5:    }  
6:    void AnotherMethod()  
7:    {    
8:      var panel = getSomeElem();  
9:      panel.Height += 100;    
10:     ...  
11:   }  

The difference between both operators is very clear. The cast immediately throws an exception whenever the object can't be cast to the specified type, while the as operator will only return null.

One could imagine that returning null is better. And actually, if the caller (or the rest of the code) is prepared to get a null, than you should go with as. But that usually isn't the case: we don't usually write code expecting stuff to be null. Moreover, the method's contract implicates that an object will be returned. 

Wait a bit! This is getting a little cryptic, right? In both operations, on the hypothesis that the object can't be cast to Panel, we will get an exception. So there's no difference, right? 

Well, there is. The question is that the operator as will hide the error. See, on the first example above, the exception thrown will be NullReferenceException at the method AnotherMethod, up in the stack, in the line "A". Whereas in the example 2, the returned exception will be InvalidCastException, exactly at the line B, exactly where the error was originated.

If  getSomeElem is a library method, it will get really difficult to understand where the exception has arisen (which was the fact that the first element of children wasn't of the expected type). A uninformed caller might come to the conclusion that the object doesn't exists (usually the case when something returns null).

In conclusion, when you need to make a cast, prefer to do it explicitly. Get used to the cast notation early. If there is a dire need to use as, then remember to always check whether the returned reference is null. Only return this value after a as operation if the method's contract explictly states that the method returns null on error.