C# elegant way to check if a property's property is null
Asked Answered
A

20

126

In C#, say that you want to pull a value off of PropertyC in this example and ObjectA, PropertyA and PropertyB can all be null.

ObjectA.PropertyA.PropertyB.PropertyC

How can I get PropertyC safely with the least amount of code?

Right now I would check:

if(ObjectA != null && ObjectA.PropertyA !=null && ObjectA.PropertyA.PropertyB != null)
{
    // safely pull off the value
    int value = objectA.PropertyA.PropertyB.PropertyC;
}

It would be nice to do something more like this (pseudo-code).

int value = ObjectA.PropertyA.PropertyB ? ObjectA.PropertyA.PropertyB : defaultVal;

Possibly even further collapsed with a null-coalescing operator.

EDIT Originally I said my second example was like js, but I changed it to psuedo-code since it was correctly pointed out that it would not work in js.

Aggappera answered 12/8, 2010 at 13:40 Comment(0)
L
165

In C# 6 you can use the Null Conditional Operator. So the original test will be:

int? value = objectA?.PropertyA?.PropertyB?.PropertyC;
Lohr answered 7/11, 2014 at 2:3 Comment(3)
could you explain what this does? What is value equal to if PropertyC is null? or if PropertyB is null? what if Object A is null?Chemosynthesis
If ANY of these properties are null, then the entire statement returns as null. It starts from left to right. Without the syntactical sugar this is equivalent to a series of if statements where if(propertyX == null) {value = null} else if (propertyY == null){ value = null} else if...... with the eventual last expression being if(propertyZ != null) { value = propertyZ }Kearney
@Kearney - or more simply, objectA == null || objectA.PropertyA == null || objectA.PropertyA.PropertyB == null ? null : objectA.PropertyA.PropertyB.PropertyC.Saunder
S
28

Short Extension Method:

public static TResult IfNotNull<TInput, TResult>(this TInput o, Func<TInput, TResult> evaluator)
  where TResult : class where TInput : class
{
  if (o == null) return null;
  return evaluator(o);
}

Using

PropertyC value = ObjectA.IfNotNull(x => x.PropertyA).IfNotNull(x => x.PropertyB).IfNotNull(x => x.PropertyC);

This simple extension method and much more you can find on http://devtalk.net/csharp/chained-null-checks-and-the-maybe-monad/

EDIT:

After using it for moment I think the proper name for this method should be IfNotNull() instead of original With().

Scuba answered 2/4, 2014 at 10:56 Comment(0)
F
16

Can you add a method to your class? If not, have you thought about using extension methods? You could create an extension method for your object type called GetPropC().

Example:

public static class MyExtensions
{
    public static int GetPropC(this MyObjectType obj, int defaltValue)
    {
        if (obj != null && obj.PropertyA != null & obj.PropertyA.PropertyB != null)
            return obj.PropertyA.PropertyB.PropertyC;
        return defaltValue;
    }
}

Usage:

int val = ObjectA.GetPropC(0); // will return PropC value, or 0 (defaltValue)

By the way, this assumes you are using .NET 3 or higher.

Finn answered 12/8, 2010 at 13:50 Comment(0)
H
12

The way you're doing it is correct.

You could use a trick like the one described here, using Linq expressions :

int value = ObjectA.NullSafeEval(x => x.PropertyA.PropertyB.PropertyC, 0);

But it's much slower that manually checking each property...

Hutto answered 12/8, 2010 at 13:48 Comment(0)
S
11

Refactor to observe the Law of Demeter

Salina answered 12/8, 2010 at 13:58 Comment(3)
I don't consider an object graph only three levels deep to be in need of refactoring when you're only reading properties. I'd agree if the OP wanted to call a method on an object referenced through PropertyC but not when it's a property that only needs checking for null before reading. In this example it could be as simple as Customer.Address.Country where Country could be a reference type such as KeyValuePair. How would you refactor this to prevent the need for the null ref checks?Sitsang
The OP example is actually 4 deep. My suggestion isn't to remove the null ref checks but to locate them in the objects most likely to be able to handle them properly. Like most "rules of thumb" there are exceptions but I'm not convinced this is one. Can we agree to disagree?Salina
I agree with @Salina (though, in fairness @Daz Lewis is proposing a 4-deep example, since the last item is a KeyValuePair). If something is messing with a Customer object, then I don't see what business it has looking through the Address object-heirarchy. Suppose you later decide that a KeyValuePair wasn't such a great idea for the Country property. In that case, everybody's code has to change. That's not a good design.Mohenjodaro
H
11

Update 2014: C# 6 has a new operator ?. various called 'safe navigation' or 'null propagating'

parent?.child

Read http://blogs.msdn.com/b/jerrynixon/archive/2014/02/26/at-last-c-is-getting-sometimes-called-the-safe-navigation-operator.aspx for details

This has long been a hugely popular request https://visualstudio.uservoice.com/forums/121579-visual-studio/suggestions/3990187-add-operator-to-c-?tracking_code=594c10a522f8e9bc987ee4a5e2c0b38d

Handley answered 20/5, 2014 at 15:4 Comment(0)
A
8

You're obviously looking for the Nullable Monad:

string result = new A().PropertyB.PropertyC.Value;

becomes

string result = from a in new A()
                from b in a.PropertyB
                from c in b.PropertyC
                select c.Value;

This returns null, if any of the nullable properties are null; otherwise, the value of Value.

class A { public B PropertyB { get; set; } }
class B { public C PropertyC { get; set; } }
class C { public string Value { get; set; } }

LINQ extension methods:

public static class NullableExtensions
{
    public static TResult SelectMany<TOuter, TInner, TResult>(
        this TOuter source,
        Func<TOuter, TInner> innerSelector,
        Func<TOuter, TInner, TResult> resultSelector)
        where TOuter : class
        where TInner : class
        where TResult : class
    {
        if (source == null) return null;
        TInner inner = innerSelector(source);
        if (inner == null) return null;
        return resultSelector(source, inner);
    }
}
Aconite answered 12/8, 2010 at 14:3 Comment(2)
Why is the extension method here? It's not being used.Price
@MladenMihajlovic: the SelectMany extension method is used by the from ... in ... from ... in ... syntax.Aconite
M
6

Assuming you have empty values of types one approach would be this:

var x = (((objectA ?? A.Empty).PropertyOfB ?? B.Empty).PropertyOfC ?? C.Empty).PropertyOfString;

I'm a big fan of C# but a very nice thing in new Java (1.7?) is the .? operator:

 var x = objectA.?PropertyOfB.?PropertyOfC.?PropertyOfString;
Macilroy answered 12/8, 2010 at 14:1 Comment(5)
Is it really going to be in Java 1.7 ? It's been requested in C# for a long time, but I doubt it will ever happen...Hutto
Unfortunately I don't have empty values. That Java syntax looks sweet though! I'm going to up-vote this, just because I want that syntax!Aggappera
Thomas: Last time I checked tech.puredanger.com/java7 it implied Java would get it. But now when I recheck it says: Null safe handling : NO. So I revoke my statement and replace it with a new one: It was proposed for Java 1.7 but didn't make it.Macilroy
An additional approach is the one used by monad.netMacilroy
Seems like the ?. operator is in Visual Studio 2015 https://msdn.microsoft.com/en-us/library/dn986595.aspxSunny
T
5

I saw something in the new C# 6.0, this is by using '?' instead of checking

for example instead of using

if (Person != null && Person.Contact!=null && Person.Contact.Address!= null && Person.Contact.Address.City != null)
{ 
  var city = person.contact.address.city;
}

you simply use

var city = person?.contact?.address?.city;

I hope it helped somebody.


UPDATE:

You could do like this now

 var city = (Person != null)? 
           ((Person.Contact!=null)? 
              ((Person.Contact.Address!= null)?
                      ((Person.Contact.Address.City!=null)? 
                                 Person.Contact.Address.City : null )
                       :null)
               :null)
            : null;
Transpadane answered 28/6, 2015 at 10:28 Comment(0)
E
4

This code is "the least amount of code", but not the best practice:

try
{
    return ObjectA.PropertyA.PropertyB.PropertyC;
}
catch(NullReferenceException)
{
     return null;
}
Earthwork answered 12/8, 2010 at 13:48 Comment(3)
I've seen code like this alot and disregarding the performance loss the biggest issue is that it complicates debugging because the real exception drowns in millions of useless null ref exceptions.Macilroy
Sometimes it's amusing to read my own answer after 3 years. I think, I would answer differently today. I would say that the code violates Demeter's law and I would recommend refactoring it so it wouldn't.Earthwork
As of today, 7 years after the original answer, I would join @Phillip Ngan and would use C# 6 with the following syntax: int? value = objectA?.PropertyA?.PropertyB?.PropertyC;Earthwork
M
4

When I need to chain calls like that, I rely on a helper method I created, TryGet():

    public static U TryGet<T, U>(this T obj, Func<T, U> func)
    {
        return obj.TryGet(func, default(U));
    }

    public static U TryGet<T, U>(this T obj, Func<T, U> func, U whenNull)
    {
        return obj == null ? whenNull : func(obj);
    }

In your case, you would use it like so:

    int value = ObjectA
        .TryGet(p => p.PropertyA)
        .TryGet(p => p.PropertyB)
        .TryGet(p => p.PropertyC, defaultVal);
Mckoy answered 8/11, 2013 at 17:37 Comment(2)
I don't think this code works. What is the type of defaultVal? var p = new Person(); Assert.AreEqual( p.TryGet(x => x.FirstName) .TryGet(x => x.LastName) .TryGet(x => x.NickName, "foo"), "foo");Ogren
The example I wrote should be read as such: ObjectA.PropertyA.PropertyB.PropertyC. Your code seems to be trying to load a property called "LastName" from "FirstName", which is not the intended usage. A more correct example would be something like: var postcode = person.TryGet(p => p.Address).TryGet(p => p.Postcode); By the way, my TryGet() helper method is very similar to a new feature in C# 6.0 - the null-conditional operator. Its usage will be like so: var postcode = person?.Address?.Postcode; msdn.microsoft.com/en-us/magazine/dn802602.aspxMckoy
M
2

You could do this:

class ObjectAType
{
    public int PropertyC
    {
        get
        {
            if (PropertyA == null)
                return 0;
            if (PropertyA.PropertyB == null)
                return 0;
            return PropertyA.PropertyB.PropertyC;
        }
    }
}



if (ObjectA != null)
{
    int value = ObjectA.PropertyC;
    ...
}

Or even better might be this:

private static int GetPropertyC(ObjectAType objectA)
{
    if (objectA == null)
        return 0;
    if (objectA.PropertyA == null)
        return 0;
    if (objectA.PropertyA.PropertyB == null)
        return 0;
    return objectA.PropertyA.PropertyB.PropertyC;
}


int value = GetPropertyC(ObjectA);
Mohenjodaro answered 12/8, 2010 at 13:57 Comment(0)
P
1

I would write your own method in the type of PropertyA (or an extension method if it's not your type) using the similar pattern to the Nullable type.

class PropertyAType
{
   public PropertyBType PropertyB {get; set; }

   public PropertyBType GetPropertyBOrDefault()
   {
       return PropertyB != null ? PropertyB : defaultValue;
   }
}
Psychochemical answered 12/8, 2010 at 13:47 Comment(1)
Well, in that case, obviously PropertyB can never be null.Violation
T
1

This approach is fairly straight-forward once you get over the lambda gobbly-gook:

public static TProperty GetPropertyOrDefault<TObject, TProperty>(this TObject model, Func<TObject, TProperty> valueFunc)  
                                                        where TObject : class
    {
        try
        {
            return valueFunc.Invoke(model);
        }
        catch (NullReferenceException nex)
        {
            return default(TProperty);
        }
    }

With usage that might look like:

ObjectA objectA = null;

Assert.AreEqual(0,objectA.GetPropertyOrDefault(prop=>prop.ObjectB.ObjectB.ObjectC.ID));

Assert.IsNull(objectA.GetPropertyOrDefault(prop => prop.ObjectB));
Trichromatic answered 21/9, 2012 at 20:7 Comment(3)
Just curious why someone would downvote 8 years after I provided a response (which was years before C# 6's null coalesce was a thing).Trichromatic
I downvoted because in C# one should not use try/catch or exceptions for control-flow: in .NET exceptions should be exceptional: if you're expecting something bad to happen then it shouldn't be represented by an exception (i.e. expected things are not exceptional) - and checking for a null-reference first is incredibly cheap, whereas exceptions in .NET are expensive.Honig
This might be a little pedantic for something that has since been solved with the null coalesce operator (.?). Anyhow, the usage still matches how you've phrased it and I'll defend it. I'm evaluating a generic delegate probably expecting to get a value, but there might be an exception where the value is null. Catching a specific type of exception is the essence of 'expecting' something bad to possibly happen and this is why specific exception types exist. Let's just all use the null coalesce and be productive. I appreciate the discourse.Trichromatic
Z
1

you can use the following extension and I think it is really good:

/// <summary>
/// Simplifies null checking
/// </summary>
public static TR Get<TF, TR>(TF t, Func<TF, TR> f)
    where TF : class
{
    return t != null ? f(t) : default(TR);
}

/// <summary>
/// Simplifies null checking
/// </summary>
public static TR Get<T1, T2, TR>(T1 p1, Func<T1, T2> p2, Func<T2, TR> p3)
    where T1 : class
    where T2 : class
{
    return Get(Get(p1, p2), p3);
}

/// <summary>
/// Simplifies null checking
/// </summary>
public static TR Get<T1, T2, T3, TR>(T1 p1, Func<T1, T2> p2, Func<T2, T3> p3, Func<T3, TR> p4)
    where T1 : class
    where T2 : class
    where T3 : class
{
    return Get(Get(Get(p1, p2), p3), p4);
}

And it is used like this:

int value = Nulify.Get(objectA, x=>x.PropertyA, x=>x.PropertyB, x=>x.PropertyC);
Zachary answered 10/12, 2013 at 7:49 Comment(0)
P
0

It is not possible.
ObjectA.PropertyA.PropertyB will fail if ObjectA is null due to null dereferencing, which is an error.

if(ObjectA != null && ObjectA.PropertyA ... works due to short circuiting, ie ObjectA.PropertyA will never be checked if ObjectA is null.

The first way you propose is the best and most clear with intent. If anything you could try to redesign without having to rely on so many nulls.

Pedaias answered 12/8, 2010 at 13:48 Comment(0)
G
0

Just stumbled accross this post.

Some time ago I made a suggestion on Visual Studio Connect about adding a new ??? operator.

http://visualstudio.uservoice.com/forums/121579-visual-studio/suggestions/4104392-add-as-an-recursive-null-reference-check-opera

This would require some work from the framework team but don't need to alter the language but just do some compiler magic. The idea was that the compiler should change this code (syntax not allowed atm)

string product_name = Order.OrderDetails[0].Product.Name ??? "no product defined";

into this code

Func<string> _get_default = () => "no product defined"; 
string product_name = Order == null 
    ? _get_default.Invoke() 
    : Order.OrderDetails[0] == null 
        ? _get_default.Invoke() 
        : Order.OrderDetails[0].Product == null 
            ? _get_default.Invoke() 
            : Order.OrderDetails[0].Product.Name ?? _get_default.Invoke()

For null check this could look like

bool isNull = (Order.OrderDetails[0].Product ??? null) == null;
Gulgee answered 9/12, 2013 at 13:26 Comment(0)
C
0

I wrote a method that accepts a default value, here is how to use it:

var teacher = new Teacher();
return teacher.GetProperty(t => t.Name);
return teacher.GetProperty(t => t.Name, "Default name");

Here is the code:

public static class Helper
{
    /// <summary>
    /// Gets a property if the object is not null.
    /// var teacher = new Teacher();
    /// return teacher.GetProperty(t => t.Name);
    /// return teacher.GetProperty(t => t.Name, "Default name");
    /// </summary>
    public static TSecond GetProperty<TFirst, TSecond>(this TFirst item1,
        Func<TFirst, TSecond> getItem2, TSecond defaultValue = default(TSecond))
    {
        if (item1 == null)
        {
            return defaultValue;
        }

        return getItem2(item1);
    }
}
Countercheck answered 8/10, 2014 at 15:2 Comment(3)
This solution has already been provided in other answers (repeatedly). There is no reason at all to be posting it again.Torr
I didn't see any that accepst a default value.Countercheck
I count 6 others that utilize a defined default value. Apparently you didn't look all that hard.Torr
S
0

Teacher teacher = new Teacher();

teacher.name = teacher.name == null ? defaultvalue : teacher.name;

Senecal answered 12/1, 2023 at 6:36 Comment(1)
Welcome to Stack Overflow! While this code may solve the question, including an explanation of how and why this solves the problem would really help to improve the quality of your post, and probably result in more up-votes. Remember that you are answering the question for readers in the future, not just the person asking now. Please edit your answer to add explanations and give an indication of what limitations and assumptions apply.Oas
L
-3
var result = nullableproperty ?? defaultvalue;

The ?? (null-coalescing operator) means if the first argument is null, return the second one instead.

Lone answered 10/5, 2013 at 13:29 Comment(3)
This answer doesn't solve the OP's problem. How would you apply the solution with ?? operator to ObjectA.PropertyA.PropertyB when all parts of the expression (ObjectA, PropertyA and PropertyB) can be null?Sleuth
True, i think i didn't even read the question at all. Anyway, impossible is nothing just don't do it :P static void Main(string[] args) { a ca = new a(); var default_value = new a() { b = new object() }; var value = (ca ?? default_value).b ?? default_value.b; } class a { public object b = null; }Wicklow
(ObjectA ?? DefaultMockedAtNull).PropertyA != null?ObjectA.PropertyA.PropertyB: nullWicklow

© 2022 - 2024 — McMap. All rights reserved.