Partager via


Maybe I'm just a little slow...

But I only just realized that extension methods are cool for avoiding NullReferenceExceptions.

We all know that if you have something like this:

LicensePlate
licensePlate = null;

Car car = licensePlate.Car;

It will throw a NullReferenceException. However if you have an extension method

public static Car GetCar(this LicensePlate
lp);

we can call it like this:

Car car = licensePlate.GetCar();

And we can still handle the fact that 'licensePlate' is null gracefully. i.e.

public static Car GetCar(this LicensePlate
lp)

{

if
(lp == null) return
null;

return
lp.Car;

}

Now imagine you wanted to chain a whole series of calls something like this:

string
code = licensePlate.Car.Owner.Address.PostCode;

That is incredibly unsafe, because any one of those properties could potentially return null.

To get around that you could use a general purpose extension method using generics, to allow you to go through a set of arbitrary translations, anyone of which could do nasty things like return null:

public static V Maybe<T, V>(this
T t, Func<T, V> selector)

where
T : class

where
V : class

{

if
(t == null) return
null;

return
selector(t);

}

Now you can use this to chain a whole load of unsafe methods together.

string
code = licensePlate.Maybe(lp => lp.Car)

.Maybe(c
=> c.Owner)

.Maybe(o
=> o.Address)

.Maybe(a
=> a.PostCode);

It looks a lot like the Maybe monad (hence the name). The interesting thing though, is that if you implement your methods as extensions methods rather than instance methods, you can wire this sort of thing in so easily, without needing to use lambdas at all.

i.e. so rather than this which is unsafe:

string
code = licensePlate.Car.Owner.Address.PostCode;

of this which is ridiculously cumbersome and hard to read:

LicensePlate
licensePlate = null;

Car car = null;

Person owner = null;

Address address = null;

string code = null;

if
(licensePlate != null)

{

car = licensePlate.Car;

if (car != null)

{

owner = car.Owner;

if
(owner != null)

{

address = owner.Address;

if
(address != null)

{

code = address.PostCode;

}

}

}

}

or this which is a little complicated for anyone uncomfortable with lambdas:

string
code = licensePlate.Maybe(lp => lp.Car)

.Maybe(c
=> c.Owner)

.Maybe(o
=> o.Address)

.Maybe(a
=> a.PostCode);

you could write this:

string code =
licensePlate.GetCar().GetOwner().GetAddress().GetPostCode();

Which is almost as easy to read as the original version. But much safer.

All you have to do is write your extension method to handle nulls.

Sometimes the coolest things are right under your nose.

Comments

  • Anonymous
    February 28, 2008
    PingBack from http://www.biosensorab.org/2008/02/28/maybe-im-just-a-little-slow/
  • Anonymous
    February 28, 2008
    Hello Alex,I love it! :-)If I remember well, I have readen an article in which they say that in C#4, we will have an operator ?. which do what you do with extension methods.
  • Anonymous
    February 28, 2008
    Qui n'a jamais fait quelque chose comme ça: LicensePlate licensePlate = null ; // Récupérer une instance
  • Anonymous
    February 28, 2008
    I made the same method just a few days ago (just called it IfNotNull but Maybe actually sounds better :P). Only difference is that I didn't use the constraint on V.public static V Maybe<T, V>(this T t, Func<T, V> selector)           where T : class{   return t != null ? selector(t) : default(V);}
  • Anonymous
    February 29, 2008
    Que les puristes de la performance soient rassurés, le reste du post n'est que pour le fun :) En discutant
  • Anonymous
    March 03, 2008
    @XUI: obviously great (excuse the self congratulation) minds think alike.@Michel: that is pretty cool.
  • Anonymous
    March 03, 2008
    Michel Prefetti , has taken my little Maybe thingie and taken it a lot further , by using expression
  • Anonymous
    March 03, 2008
    Michel Prefetti , has taken my little Maybe thingie and gone a lot further , by using expression tree
  • Anonymous
    March 06, 2008
    The comment has been removed