.Net Tips — using as vs casting in C#

Run­time type con­ver­sion is some­thing we all have to do from time to time, and in C#, type con­ver­sion is usu­al­ly done using either the as key­word or cast­ing.

Here’s a quick glance of how the two approach­es dif­fer:

Null ref­er­ence Con­ver­sion fail­ure User-defined con­ver­sion Per­for­mance
as Null Null Ignored Fast
cast­ing Null Excep­tion Used Slow

Pre­fer as to cast­ing

In Bill Wagner’s Effec­tive C# book, he rec­om­mends that you should use the as key­word when­ev­er pos­si­ble, because:

  • it’s more effi­cient at run­time, the as key­word (like the is key­word) does not per­form any user-defined con­ver­sion. It will only suc­ceed if the object is of the sought type (or derived from it) and nev­er cre­ate a new object to sat­is­fy a request.
  • requires less code because you don’t need a try-catch block in addi­tion to a null check.

There are a few things you should keep in mind when using the as key­word:

  • it doesn’t work with val­ue types because val­ue types can nev­er be null
  • don’t use the is key­word if you’re using as for type con­ver­sion, it’s redun­dant because these two state­ments are equiv­a­lent except the as ver­sion eval­u­ates expres­sion only once!
expression as type
expression is type ? (type) expression : (type) null

When you should use cast­ing

That’s not to say that you should nev­er use cast­ing, a typ­i­cal use I have found work­ing with a Flash client is the need to con­vert busi­ness log­ic class­es into AMFVO which are sim­ple con­tain­ers for a sub­set of the prop­er­ties of the log­ic class­es and none of the behav­iour. In almost all cas­es like this I over­load the implicit/explicit oper­a­tor (see post on con­trol­ling type con­ver­sion here) and use cast­ing to invoke my cus­tom type con­ver­sions. The as key­word would not be appro­pri­ate in this case because the val­ue object class­es should nev­er inher­it from the busi­ness log­ic class­es.

Gotcha!

Yup, there’s a gotcha with using cast­ing, and a pret­ty big one at that too!

The user-defined con­ver­sion oper­a­tors oper­ate only on the com­pile-time type of an object. What this means is that if no con­ver­sion oper­a­tor exists for the declared type of a vari­able the cast­ing oper­a­tion will fail even if a con­ver­sion oper­a­tor exists for the run­time type of that vari­able. I.E.

void Main()
{
    Player player = new Player { Name = "Me" };
    PlayerDTO playerDTO = (PlayerDTO) player; // this succeeds

    object player2 = new Player { Name = "You" };
    PlayerDTO playerDTO2 = (PlayerDTO) player2; // this fails, throws runtime InvalidCastException

    object player3 = new Player { Name = "They" };
    PlayerDTO playerDTO3 = player3 as PlayerDTO; // this fails, returns null

    Player player4 = new Player { Name = "It" };
    PlayerDTO playerDTO4 = player4 as PlayerDTO; // this won't compile

}

public class Player
{
    public string Name { get; set; }

    public static explicit operator PlayerDTO(Player player)
    {
        return new PlayerDTO(player.Name);
    }
}

public class PlayerDTO
{
    public PlayerDTO(string name)
    {
        Name = name;
    }

    public string Name { get; private set; }
}