Saturday, 3 November 2012

Safe dereferencing in C#

There's one feature in Groovy that I really miss in other languages, the Safe dereference operator. (name = p?.Name)
It's amazing how such a simple thing spares you cluttering your code. This C# (or Java) code:

if (person != null && person.Address != null)
Console.WriteLine("person.Address.City: " + person.Address.City);
else
Console.WriteLine("");

can be written in Groovy using the "?." safe dereference operator just like this:

println person?.Address?.City

In the last days I've had to write in C# a good bunch of ugly looking code like the above, which made me raise 2 questions:

  1. Why haven't the cool guys in the C# team added something like "?." to the language?
  2. Can't I find a workaround?

For the second question, I quickly came up with an apparently rather usable solution, but once I started to code it I realized it was not that perfect because of the Reference types, Value types, Nullable types differences... In fact, maybe that's the reason for not having "?." in the language itself.
Anyway, I think my solutions is usable enough to share it here and add it to my toolbox:

public static TResult SafeGet<T, TResult> (this T target, Func<T, TResult> getter) where T: class
 {
  //Type of conditional expression cannot be determined because there is no implicit conversion between 'TResult' and '<null>'
  //return target != null ? getter(target) : null; 
  return target != null ? getter(target) : default(TResult); 
 }

that we can use like this:

Console.WriteLine("p1.Address.City: " + 
   p1.SafeGet(item => item.Address)
   .SafeGet(item => item.City)
  );

instead of this:

  var city = null;
  if (p1 != null && p1.Address != null)
   city = p1.Address.City;
  Console.WriteLine("p1.Address.City: " + city);

Some notes about that so simple code:

  • I'm using the "where T:class" generic constraint, forcing T to be a Reference type. I would also like to accept Nullable types there, but these are considered value types, so I have no way to indicate that in the constraint, which means that unfortunately this solution won't work for Nullable types
  • I initially intended to return null:
    return target != null ? getter(target) : null;
    but then we would get the infamous compilation error:

    Type of conditional expression cannot be determined because there is no implicit conversion between 'TResult' and ''

    The compiler can't be sure that the type being returned there can be null (again for this we would need to be able to indicate a nullable constraint)... so in the end I've changed it to default

I then had a look to see what solutions others had thought up for this same issue, and found one guy following a very similar approach

You can download a sample here

No comments:

Post a Comment