Wednesday, July 8, 2009

Summary for Meeting 3: Nullables<T>

Summary for Meeting 3: Nullables<T>

This is a rather short chapter and we didn’t expect very much new information. But as always when you start to look at things and start to explain them to each other interesting points pop up.

  • I was not aware of the GetValueOrDefault method on Nullable<T>. Never used it.
  • Boxing/Unboxing of Nullables: 4.2.2 pp 118, 119: I had no idea that this might be an issue. But that boxing an instance of Nullable<T> results in either a null reference or a boxed value of the underlying type does make perfect sense.
  • The class Nullable. Never used it as well. But I should have used the GetUnderlyingType method. I firmly believe that I have implemented exactly that feature two or three times in the last couple of years in various projects using reflection.
  • Then we wondered why one would need the static class Nullable with its Compare and Equals method. We first thought (actually I did and I was completely wrong) that when using the instance method Equals of a concrete Nullable<T> instance it would throw a NullReferenceException when the instance had no value. But as Nullable<T> is a value type and not a reference type it in itself can never be null and it handles the case described without any problems.
    The Nullable static class just delegates the main work to the objects returned by Comparer<T>.Default and EqualityComparer<T>.Default (where T is the underlying type of the Nullable) in the cases that both have instances have a value and dealing with the case that one or the other or even both of them are null.
  • No surprise for me personally about the null coalescing operator. But as Jon Skeet states in his book, I notice that it is one of the lesser known features of C#. My estimation is that 2 out of 3 developers do not know it. (As an anecdote I had a code review with an architect and had to explain that operator to him). Even though I used it for years I never thought of the inventive ideas that Jon Skeet explains. Have a look further down down in this post for more on the null coalescing operator.
  • Using Nullable<T> for trying an operation: That idea is brilliant. Why didn't I think of it? I used it right away. The main point that instead of using a method with a Boolean return value and a out parameter for the result (like int.TryParse) you return a Nullable<T> and if the operation succeeded you return a Nullable<T> with the value you are interested in. If the operation did not succeed,  you return null.

 

Here are some further remarks on the null coalescing operator.

Null coalescing operator with objects of various types

The first scenario is out of the GUI lay of an application I am just working on. Lets say we need to find out if a drag and drop operation is possible.  One idea was to write code like the following:

User u = someObject as User;
Role r = someObject as Role;
Title t = someObject as Title;

// won’t work!
object o = u ?? r ?? t;

if (o != null) PerformDragDropAction();

As indicated by the comment, this will not work. The null coalescing operator is not very happy if it is applied on variables of different types.

As it turns out casting the last variable to object will be accepted by the compiler.

// but this works
object o = u ?? r ?? (object)t;

You can even cast the next to last variable used in the chain of operators..

// this as well, but it’s even uglier
object o = u ?? (object)r ?? t;

Null coalescing operator for very short property initializers

One thing that I find very unpleasant is the lazy initialization of property values.

What you usually write is something like this (and yes I know: When you are using an IoC container, you are very unlikely to write such code)

private ILogger logger;
public ILogger Logger

    get 
    { 
        if (logger == null)
        {
            logger = new Logger();
        }
        return logger; 
    }
}

But you can do it with very little code in this way:

private ILogger logger;
public ILogger Logger
{
    get { return logger ?? (logger = new Logger()); }
}

This works, because the result of an assignment seems to be the assigned value.

This code is very short. Its main problem is the fact that it is not as idiomatic as the first, longer version. Most people will take much longer to comprehend what is going on when reading the second version in comparison to the first.