14

Before I explain what I want to do, if you look at the following code, would you understand what it's supposed to do? (updated - see below)

Console.WriteLine(
  Coalesce.UntilNull(getSomeFoo(), f => f.Value) ?? "default value");

C# already has a null-coalescing operator that works quite well on simple objects but doesn't help if you need to access a member of that object.

E.g.

Console.WriteLine(getSomeString()??"default");

works very well but it won't help you here:

public class Foo
{
  public Foo(string value) { Value=value; }
  public string Value { get; private set; }
}

// this will obviously fail if null was returned
Console.WriteLine(getSomeFoo().Value??"default"); 

// this was the intention
Foo foo=getSomeFoo();
Console.WriteLine(foo!=null?foo.Value:"default");

Since this is something that I come across quite often I thought about using an extension method (old version):

public static class Extension
{
  public static TResult Coalesce<T, TResult>(this T obj, Func<T, TResult> func, TResult defaultValue)
  {
    if (obj!=null) return func(obj);
    else return defaultValue;
  }

  public static TResult Coalesce<T, TResult>(this T obj, Func<T, TResult> func, Func<TResult> defaultFunc)
  {
    if (obj!=null) return func(obj);
    else return defaultFunc();
  }
}

Which allows me to write:

Console.WriteLine(getSomeFoo().Coalesce(f => f.Value, "default value"));

So would you consider this code to be readable? Is Coalesce a good name?

Edit 1: removed the brackets as suggested by Marc

Update

I really liked lassevk's suggestions and Groo's feedback. So I added overloads and didn't implement it as an extension method. I also decided that defaultValue was redundant because you could just use the existing ?? operator for that.

This is the revised class:

public static class Coalesce
{
  public static TResult UntilNull<T, TResult>(T obj, Func<T, TResult> func) where TResult : class
  {
    if (obj!=null) return func(obj);
    else return null;
  }

  public static TResult UntilNull<T1, T2, TResult>(T1 obj, Func<T1, T2> func1, Func<T2, TResult> func2) where TResult : class
  {
    if (obj!=null) return UntilNull(func1(obj), func2);
    else return null;
  }

  public static TResult UntilNull<T1, T2, T3, TResult>(T1 obj, Func<T1, T2> func1, Func<T2, T3> func2, Func<T3, TResult> func3) where TResult : class
  {
    if (obj!=null) return UntilNull(func1(obj), func2, func3);
    else return null;
  }

  public static TResult UntilNull<T1, T2, T3, T4, TResult>(T1 obj, Func<T1, T2> func1, Func<T2, T3> func2, Func<T3, T4> func3, Func<T4, TResult> func4) where TResult : class
  {
    if (obj!=null) return UntilNull(func1(obj), func2, func3, func4);
    else return null;
  }
}

Sample usage:

Console.WriteLine(
  Coalesce.UntilNull(getSomeFoo(), f => f.Value) ?? "default value");

Another sample:

public class Bar
{
  public Bar Child { get; set; }
  public Foo Foo { get; set; }
}

Bar bar=new Bar { Child=new Bar { Foo=new Foo("value") } };

// prints "value":
Console.WriteLine(
  Coalesce.UntilNull(bar, b => b.Child, b => b.Foo, f => f.Value) ?? "null");

// prints "null":
Console.WriteLine(
  Coalesce.UntilNull(bar, b => b.Foo, f => f.Value) ?? "null");
laktak
  • 57,064
  • 17
  • 134
  • 164
  • 1
    Wow! I knew that ext. methods are basically "thiscall" static methods, but I still thought that they would throw a Null-reference exception when invoked on a null instance (because non-virtual instance methods actually work the same way)... I guess it has more sense this way anyway. – vgru Mar 20 '09 at 13:21
  • Thanks for your comment! This is exactly what I was looking for - or rather not looking for ;) An API should follow the "The Principle of Least Surprise" - so I guess using extension methods is not a good idea - I'll rethink my design! – laktak Mar 21 '09 at 09:56
  • See my update - C# 6 may well have what you want :) – Jon Skeet Jul 21 '15 at 17:50
  • Thanks, already stumbled over it here https://github.com/dotnet/roslyn/wiki/New-Language-Features-in-C%23-6#null-conditional-operators ;) – laktak Jul 21 '15 at 17:52

8 Answers8

11

Yes, I would understand it. Yes, coalesce is a good name. Yes, it would be better if C# had a null-safe dereferencing operator like Groovy and some other languages :)

Update

C# 6 has such an operator - the null conditional operator, ?. For example:

var street = customer?.PrimaryAddress?.Street;

Use it in conjunction with the original null-coalescing operator if you still want a default. For example:

var street = customer?.PrimaryAddress?.Street ?? "(no address given)";

Or, based on the original code in the question:

Console.WriteLine(getSomeFoo()?.Value ?? "default"); 

Noting, of course, that providing a default this way works only if it's okay to use that default value even when the final property value is available but set to null for some reason.

The result of an expression x?.y is null if x evaluates to null; otherwise it's the result of x.y. Oh, and you can use it for conditional method invocation, too:

possiblyNull?.SomeMethod();
Peter Duniho
  • 68,759
  • 7
  • 102
  • 136
Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
7

It confused me already... normally, you think of coalesce acting on its values - I imagined that the first non-null of (f) => f.Value, and "default value" would be returned, which isn't the case (the null test is on the originating instance).

Note it would be clearer without the brackets?

f => f.Value

What you are actually doing is similar to Select - so something like SafeSelect would be a good name, IMO (but maybe not exactly that...).

Or even simply Dereference, as long as the argument name (etc) makes it clear what the second arg is for.

Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
  • Thanks - removed the brackets. I think SafeSelect would suggest an enumeration which it isn't ... on Dereference I'm not sure ... the hardest part is always naming things :) – laktak Mar 20 '09 at 13:15
  • I have to agree with Marc, your extension method does not act like the ?? operator in your code sample. – Samuel Mar 20 '09 at 13:49
3

This could easily be extended as well:

public static TResult Coalesce<T, TResult>(this T obj, Func<T, TResult> func, TResult defaultValue)
{
    if (obj == null)
        return defaultValue;

    return func(obj);
}

public static TResult Coalesce<T1, T2, TResult>(this T1 obj, Func<T1, T2> func1, Func<T2, TResult> func2, TResult defaultValue)
{
    if (obj == null)
        return defaultValue;

    T2 obj2 = func1(obj);
    if (obj2 == null)
        return defaultValue;

    return func2(obj2);
}

public static TResult Coalesce<T1, T2, T3, TResult>(this T1 obj, Func<T1, T2> func1, Func<T2, T3> func2, Func<T3, TResult> func3, TResult defaultValue)
{
    if (obj == null)
        return defaultValue;

    T2 obj2 = func1(obj);
    if (obj2 == null)
        return defaultValue;

    T3 obj3 = func2(obj2);
    if (obj3 == null)
        return defaultValue;

    return func3(obj3);
}

public static TResult Coalesce<T1, T2, T3, T4, TResult>(this T1 obj, Func<T1, T2> func1, Func<T2, T3> func2, Func<T3, T4> func3, Func<T4, TResult> func4, TResult defaultValue)
{
    if (obj == null)
        return defaultValue;

    T2 obj2 = func1(obj);
    if (obj2 == null)
        return defaultValue;

    T3 obj3 = func2(obj2);
    if (obj3 == null)
        return defaultValue;

    T4 obj4 = func3(obj3);
    if (obj4 == null)
        return defaultValue;

    return func4(obj4);
}

Which could be used like this:

BinaryTreeNode node = LocateNode(someKey);
BinaryTreeNode grandFatherNode = node.Coalesce(n1 => n1.Parent, n2 => n2.Parent, null);

Which would replace:

BinaryTreeNode grandFatherNode = node.Parent.Parent; // or null if none
Lasse V. Karlsen
  • 380,855
  • 102
  • 628
  • 825
2

Seems readable enough, though it's still a bit clumsy.

This seems like a perfect opportunity to implement the null object pattern though.

Consider:

public class Foo
{
  public Foo(string value) { Value=value; }
  public string Value { get; private set; }
  private static Foo nullFoo = new Foo("default value");
  public static Foo NullFoo { get { return nullFoo; } }
}

Then have getSomeFoo() return Foo.NullFoo instead of a null. It requires a little extra thought, but generally makes for nicer code.

Update in response to comments:

Let's say you don't control Foo, you can still (often) do this (which is more how you'd want to implement it regardless):

public class NullFoo : Foo
{
    private NullFoo() : base("default value") { }
    private static NullFoo instance = new NullFoo();
    public static Foo Instance { get { return instance; } }
}

Then return NullFoo.Instance from getSomeFoo(). If you don't control getSomeFoo() either, you yet still have the option to do this:

Console.WriteLine((getSomeFoo() ?? NullFoo.Instance).Value);
Wedge
  • 19,513
  • 7
  • 48
  • 71
  • That forces immutability, though (at the minimum, popsicle immutability) - and doesn't play nicely with "default(T)" etc. Plus it more generally assumes that you control the type in question (which isn't always the case) – Marc Gravell Mar 20 '09 at 10:36
1

Great post! I updated your code to support returning Nullable because Nullable is implemented as a struct.

    public static class Coalesce
    {
        public static TResult UntilNull<T, TResult>(T obj, Func<T, TResult> func) where TResult : class
        {
            if (obj != null) return func(obj);
            else return null;
        }

        public static TResult UntilNull<T1, T2, TResult>(T1 obj, Func<T1, T2> func1, Func<T2, TResult> func2) where TResult : class
        {
            if (obj != null) return UntilNull(func1(obj), func2);
            else return null;
        }

        public static TResult UntilNull<T1, T2, T3, TResult>(T1 obj, Func<T1, T2> func1, Func<T2, T3> func2, Func<T3, TResult> func3) where TResult : class
        {
            if (obj != null) return UntilNull(func1(obj), func2, func3);
            else return null;
        }

        public static TResult UntilNull<T1, T2, T3, T4, TResult>(T1 obj, Func<T1, T2> func1, Func<T2, T3> func2, Func<T3, T4> func3, Func<T4, TResult> func4) where TResult : class
        {
            if (obj != null) return UntilNull(func1(obj), func2, func3, func4);
            else return null;
        }

        public static Nullable<TResult> UntilNull<T, TResult>(T obj, Func<T, Nullable<TResult>> func) where TResult : struct
        {
            if (obj != null) return func(obj);
            else return new Nullable<TResult>();
        }

        public static Nullable<TResult> UntilNull<T1, T2, TResult>(T1 obj, Func<T1, T2> func1, Func<T2, Nullable<TResult>> func2) where TResult : struct
        {
            if (obj != null) return UntilNull(func1(obj), func2);
            else return new Nullable<TResult>();
        }

        public static Nullable<TResult> UntilNull<T1, T2, T3, TResult>(T1 obj, Func<T1, T2> func1, Func<T2, T3> func2, Func<T3, Nullable<TResult>> func3) where TResult : struct
        {
            if (obj != null) return UntilNull(func1(obj), func2, func3);
            else return new Nullable<TResult>();
        }

        public static Nullable<TResult> UntilNull<T1, T2, T3, T4, TResult>(T1 obj, Func<T1, T2> func1, Func<T2, T3> func2, Func<T3, T4> func3, Func<T4, Nullable<TResult>> func4) where TResult : struct
        {
            if (obj != null) return UntilNull(func1(obj), func2, func3, func4);
            else return new Nullable<TResult>();
        }
    }
James
  • 1,085
  • 9
  • 16
1

If you use it often in a code base, I think it is good as it is not too hard to understand on first reading and reduced the size of the code – so helps me to see the wood from the trees.

However if is only used 1 or 2 times, I think an "in line" if will be better, as I don’t see to think about the meaning of a “if” the first time I see it.

By "in line" if - I mean a normal if statement that has not be hidden in a seperate method.

Ian Ringrose
  • 51,220
  • 55
  • 213
  • 317
0

Why not write down normal coalesce function, then you could use it like this:

coalesce(something, something_else, "default");

In other words -- what do you need lambdas for?

greenoldman
  • 16,895
  • 26
  • 119
  • 185
  • He needs lambdas because `something_else` is a property of `something`. Wrapping the property dereference in a lambda allows him to delay evaluating it until he knows it is safe to do so. – Sean Devlin Feb 01 '10 at 21:48
  • 1
    Wrong approach then, because you push knowledge of that to primitive party which should be black box. If you want lazy eval just wrap your property with lazy eval class and pass such mix to the method (coalesce here). Otherwise you would end up with coalesce, coalesce_lazy, etc. – greenoldman Feb 02 '10 at 07:05
0

Six years later and Null-conditional operators are here:

Sometimes code tends to drown a bit in null-checking. The null-conditional operator lets you access members and elements only when the receiver is not-null, providing a null result otherwise:

int? length = customers?.Length; // null if customers is null Customer
first = customers?[0];  // null if customers is null

The null-conditional operator is conveniently used together with the null coalescing operator ??:

int length = customers?.Length ?? 0; // 0 if customers is null

The null-conditional operator exhibits short-circuiting behavior, where an immediately following chain of member accesses, element accesses and invocations will only be executed if the original receiver was not null:

int? first = customers?[0].Orders.Count();

This example is essentially equivalent to:

int? first = (customers != null) ? customers[0].Orders.Count() : null;

Except that customers is only evaluated once. None of the member accesses, element accesses and invocations immediately following the ? are executed unless customers has a non-null value.

Of course null-conditional operators can themselves be chained, in case there is a need to check for null more than once in a chain:

int? first = customers?[0].Orders?.Count();

Note that an invocation (a parenthesized argument list) cannot immediately follow the ? operator – that would lead to too many syntactic ambiguities. Thus, the straightforward way of calling a delegate only if it’s there does not work. However, you can do it via the Invoke method on the delegate:

if (predicate?.Invoke(e) ?? false) { … }

We expect that a very common use of this pattern will be for triggering events:

PropertyChanged?.Invoke(this, args);

This is an easy and thread-safe way to check for null before you trigger an event. The reason it’s thread-safe is that the feature evaluates the left-hand side only once, and keeps it in a temporary variable.

laktak
  • 57,064
  • 17
  • 134
  • 164