5

I am using this solution to do a chained null check in my code

Cleaner way to do a null check in C#?

I was just wondering cant we make it like this.

bool returnValue = Helper.IsNull(nullPerson.contact.address.city);

Wouldn't that be even more cleaner ?

I tried writing such a generic function

public static bool IsNull<T>(this T rootObj)
{
  var visitor = new IsNullExpressionVisitor();
  //...
  //...
}

but then I got stuck on how to make expression out of this rootObject.

Community
  • 1
  • 1
Pankaj
  • 2,618
  • 3
  • 25
  • 47
  • 2
    I don't think that there is any way of doing it with that signature. But you may have better luck passing in a lambda. like this: IsNull(() => nullPerson.contact.address.city). – Jack Ryan Apr 10 '14 at 11:51
  • Depending on the dotnet version you are using, you can use the static class Contract http://msdn.microsoft.com/en-us/library/system.diagnostics.contracts.contract.aspx – Noone Apr 10 '14 at 11:52
  • What would you expect as return value in the chain `nullPerson.contact.address.city` if there is a null value? – samy Apr 10 '14 at 11:54
  • 2
    Try this: http://www.codeproject.com/Articles/109026/Chained-null-checks-and-the-Maybe-monad – Maarten Apr 10 '14 at 11:58
  • Doesn't really address anything doing that, just moving / hiding the real issue. – Tony Hopkinson Apr 10 '14 at 11:59
  • There's a new project on github that does pretty much what you're looking for: https://github.com/devshorts/MonadicNull EDIT: So your usage would be: `var result = Option.Safe(() => nullPerson.contact.address.city); bool isNull = !result.HasValue;` – Chris Sinclair Apr 10 '14 at 12:08
  • @TonyHopkinson what real issue? – Rawling Apr 10 '14 at 12:08
  • @Rawling, chaining nulls.... – Tony Hopkinson Apr 10 '14 at 12:11
  • @TonyHopkinson What makes you think `person`, `contact` or `address` being null is an issue for OP? – Rawling Apr 10 '14 at 12:17
  • @Rawling, well the question sort of gave it away for me. If it isn't about anyone of them being null, then I've seriously misunderstood it. executing A.B.C.D as the argument to Helper.IsNull will exhibit the difficulty Help.IsNull is meant to solve. – Tony Hopkinson Apr 10 '14 at 12:25
  • 1
    @TonyHopkinson Well obviously OP wants to handle one of his chain being null, but why do you call this "moving/hiding the real issue"? What's the *real* issue? – Rawling Apr 10 '14 at 12:28
  • You are losing me. All I was pointing out was that passing code that will fail as an argument to a generic function doesn't qualify as any definition of elegant I've ever come across. The real problem is A.B.C.D will fail if any of A, B or C is null. Short of swallowing the exception inside of Helper.IsNull, it's pointless – Tony Hopkinson Apr 10 '14 at 12:39
  • In fact even that wouldn't help would it... – Tony Hopkinson Apr 10 '14 at 12:44
  • Thanks everyone, The closest to I was looking for & I reached is IsNull(() => nullPerson.contact.address.city). Thanks @JackRyan – Pankaj Apr 10 '14 at 14:20

4 Answers4

4

One way to approach this (although still somewhat clunky) is to use a construct that's sometimes called a "Maybe" monad.

Using that, the code would become something like this (which you may or may not prefer!):

string city = nullPerson.With(x => x.address)
                        .With(x => x.city);

However, rejoice because C# is getting the "Safe Navigation" operator (?.) in the next version*.

With this new ?. operator, the code would look like this:

city = nullPerson?.address?.city;

(*Allegedly...)

Matthew Watson
  • 104,400
  • 10
  • 158
  • 276
1

After reading many questions trying to solve the same problem, I would go out on a limb and say that there is currently no way to create really elegant chained null check, where elegant would mean not adding indirection to each component of the chain.

Wrapping the chain in a lambda evaluation is perhaps the least terrible way to do it at the time, and it would only work in:

CheckNull<string>(() => nullPerson.contact.address.city);

T CheckNull<T>(Func<T> f) { try { return f(); } catch { return default(T); } }

I'd rather encourage you to try and follow the Law of Demeter that says that your object shouldn't know about the inner workings of the objects it is working with and ask them to return the value any way they want to instead of going down the class tree.

Community
  • 1
  • 1
samy
  • 14,832
  • 2
  • 54
  • 82
  • It's actually bad practice to use catch besides processing errors=) But your method very close to my solution: http://stackoverflow.com/a/34086283/4711853 – Roma Borodov Dec 04 '15 at 10:53
0

No there isn't. All the existing solutions I have seen are far more horrible than the problem. A lot of the clever solutions just make it difficult for other people to understand what the code is doing.

The situation is that you want to do something like this:

city = selectedPerson.Contact.Address.City;

What you can do is encapsulate the checks in a property

City SelectedCity
{
    get
    {
       if (selectedPerson == null || selectedPerson.Contact == null || selectedPerson.Contact.Address == null)
           return null;
       return selectedPerson.Contact.Address.City;
    }
}

After-all by the very nature of wanting to access this long chain of properties you do have a concept of a selected City. The code isn't elegant but at least it's hidden away in one place and anyone reading it can immediately grasp what a "SelectedCity" is.

Weyland Yutani
  • 4,682
  • 1
  • 22
  • 28
0

Check my answer here

https://stackoverflow.com/a/34086283/4711853

you could simply write small extension method, which afford you to write chained lambda like this:

var val = instance.DefaultOrValue(x => x.SecondInstance) .DefaultOrValue(x => x.ThirdInstance) .DefaultOrValue(x => x.Value);

you may name this method shortier

Community
  • 1
  • 1
Roma Borodov
  • 596
  • 4
  • 10