tags:

views:

625

answers:

8

How can I check for nulls in a deep lamda expression?

Say for example I have a class structure that was nested several layers deep, and I wanted to execute the following lambda:

x => x.Two.Three.Four.Foo

I want it to return null if Two, Three, or Four were null, rather than throwing a System.NullReferenceException.

public class Tests
{
    // This test will succeed
    [Fact]
    public void ReturnsValueWhenClass2NotNull()
    {
        var one = new One();
        one.Two = new Two();
        one.Two.Three = new Three();
        one.Two.Three.Four = new Four();
        one.Two.Three.Four.Foo = "blah";

        var result = GetValue(one, x => x.Two.Three.Four.Foo);

        Assert.Equal("blah", result);
    }

    // This test will fail
    [Fact]
    public void ReturnsNullWhenClass2IsNull()
    {
        var one = new One();

        var result = GetValue(one, x => x.Two.Three.Four.Foo);

        Assert.Equal(null, result);
    }

    private TResult GetValue<TModel, TResult>(TModel model, Expression<Func<TModel, TResult>> expression)
    {
        var func = expression.Compile();
        var value = func(model);
        return value;
    }

    public class One
    {
        public Two Two { get; set; }
    }

    public class Two
    {
        public Three Three { get; set; }
    }

    public class Three
    {
        public Four Four { get; set; }
    }

    public class Four
    {
        public string Foo { get; set; }
        public string Bar { get; set; }
    }
}

UPDATE:

One solution would be to catch the NullReferenceException like this:

    private TResult GetValue<TModel, TResult>(TModel model, Expression<Func<TModel, TResult>> expression)
    {
        TResult value;
        try
        {
            var func = expression.Compile();
            value = func(model);
        }
        catch (NullReferenceException)
        {
            value = default(TResult);
        }
        return value;
    }

But I hate to incur the expense of catching an exception that is not, in my mind, exceptional. I expect this to be the case quite often in my domain.

UPDATE 2:

Another solution would be modify the property getters like this:

    public class One
    {
        private Two two;
        public Two Two
        {
            get
            {
                return two ?? new Two();
            }
            set
            {
                two = value;
            }
        }
    }

Which is mostly ok for my domain, but there are times when I really to expect a property to return null. I checked the answer from Josh E as helpful since it comes pretty close to what I need in some cases.

+3  A: 

You could do this with a generic helper extension method, something like:

public static class Get {
 public static T IfNotNull<T, U>(this U item, Func<U, T> lambda) where U: class {
  if (item == null) {
   return default(T);
  }
  return lambda(item);
 }
}

var one = new One();
string fooIfNotNull = one.IfNotNull(x => x.Two).IfNotNull(x => x.Three).IfNotNull(x => x.Four).IfNotNull(x => x.Foo);
Lucero
In that case, I'd want to return the default value for whatever type Foo or Bar were. What I really want to avoid is the exception if something further up in the expression tree was null.
JohnRudolfLewis
I edited my answer and added a code sample, which compiles fine and should do the trick.
Lucero
A: 

Always initialize your properties before using them. Add a constructor to class One, Two, Three and Four. In the constructor initialize your properties so they are not null.

Wallstreet Programmer
I usually do, but in this domain, the properties can get set to null sometimes. This is valid behavior.
JohnRudolfLewis
+6  A: 

You can't do that in a concise way. You can either make the lambda multiple lines, or use nested ternary operators:

var result = GetValue(one, x => x.Two == null ? null :
                                x.Two.Three == null ? null :
                                x.Two.Three.Four == null ? null :
                                x.Two.Three.Four.Foo;

Ugly, I know.

Gabe Moothart
this is far more concise one.Maybe(x=>x.Two.Three.Four.Foo); see http://maybe.codeplex.com/
Maslow
A: 

You could modify your getters to read something like:

private Two _two;
public Two Two
{
     get 
     {
       if (null == _two)
         return new Two();
       else
         return _two;
      }
}
Josh E
Modifying the implementation to save some lines in the client code should fire all kinds of alarms.
krusty.ar
I tend to disagree that this is an issue: I would call this defensive coding. The code above ensures that the value of a property is never null without sharing that knowledge with any consumer of that property / object.
Josh E
If I keep calling Two while _two is null, I keep getting *new* instances of Two... ew
Lucas
good point. In that case, you could modify it to set _two to a new Two() instance before returning it, e.g.if (null == _two) _two = new Two();return _two;
Josh E
+1  A: 

I'm not skilled in c#, but maybe there's some way to implement the "andand" pattern from ruby that solves exactly this problem without polluting the implementation.

The concept is also known as the Maybe Monad in Haskell.

The title of this article seems promising.

krusty.ar
Interesting, the article is almost identical to the solution I came up by thinking about it, see my post...
Lucero
Wow, completely missed it, I guess I was looking for the word "maybe"
krusty.ar
http://maybe.codeplex.com/ can do it.
Maslow
A: 

I find the coalesce operator useful for this at times. This only helps though if there is a default/null equivalent version of the object you can drop in.

For instance, sometimes when I'm cracking open XML...

IEnumeratable<XElement> sample;
sample.Where(S => (S.Attribute["name"] ?? new XAttribute("name","")).Value.StartsWith("Hello"))...

Depending on how the default objects are retrieved this can be verbose, and the above example is not a great use but you get the idea. For the particular case of reading XML attributes I have an extension method that returns the attribute value or an empty string.

Frank Schwieterman
+4  A: 

Doing this concisely requires an as-yet-unimplemented operator. We considered adding an operator ".?" to C# 4.0 which would have your desired semantics, but unfortunately it did not fit into our budget. We'll consider it for a hypothetical future version of the language.

Eric Lippert
This would be great! Delphi prism does the same with its ":" operator: http://prismwiki.codegear.com/en/Colon_Operator
Gabe Moothart
+1  A: 

You can now do this the project is on codeplex at http://maybe.codeplex.com/

Syntax is

string result=One.Maybe(o=>o.Two.Three.Four.Foo);

string cityName= Employee.Maybe(e=>e.Person.Address.CityName);

Maslow