Ralph Shillington
Ralph Shillington

Reputation: 21098

Is there a handy syntax to return null rather than exception when accessing a property of a null object

Consider this simple c# example:

var person = new Person {Name = "Fred", MailingAddress=null };
var result = String.Format("{0} lives at {1}",person.Name, person.MailingAddress.Street);

clearly this will throw a NullReferenceException because the MailingAddress proptery is null.

I could rewrite the second line as:

var result = String.Format("{0} lives at {1}", person.Name, person.MailingAddress == null ? (String)null : person.MailingAddress.Street);

Is there a simpler way to say express this?

Upvotes: 4

Views: 166

Answers (4)

sehe
sehe

Reputation: 393084

You could use a device based on expression trees, so you'd write

var n = person.NullPropagate(p => p.Contact.MailingAddress.StreetAddress.Number);

/* having the effect of:

   (person == null)
   ? defaultValue
   : (person.Contact == null)
     ? defaultValue
     : (person.Contact.MailingAddress == null)
       ? defaultValue
       : (person.Contact.MailingAddress.StreetAddress == null)
         ? defaultValue
         : person.Contact.MailingAddress.StreetAddress.Number;
*/

Disclaimer: I haven't written this code, I just don't know where I originally found it either. Anyone recognize this helper?

public static R NullPropagate<T, R>(this T source, Expression<Func<T, R>> expression, R defaultValue)
{
    var safeExp = Expression.Lambda<Func<T, R>>(
        WrapNullSafe(expression.Body, Expression.Constant(defaultValue)),
        expression.Parameters[0]);

    var safeDelegate = safeExp.Compile();
    return safeDelegate(source);
}

private static Expression WrapNullSafe(Expression expr, Expression defaultValue)
{
    Expression obj;
    Expression safe = expr;

    while (!IsNullSafe(expr, out obj))
    {
        var isNull = Expression.Equal(obj, Expression.Constant(null));

        safe = Expression.Condition (isNull, defaultValue, safe);

        expr = obj;
    }
    return safe;
}

private static bool IsNullSafe(Expression expr, out Expression nullableObject)
{
    nullableObject = null;

    if (expr is MemberExpression || expr is MethodCallExpression)
    {
        Expression obj;
        MemberExpression memberExpr = expr as MemberExpression;
        MethodCallExpression callExpr = expr as MethodCallExpression;

        if (memberExpr != null)
        {
            // Static fields don't require an instance
            FieldInfo field = memberExpr.Member as FieldInfo;
            if (field != null && field.IsStatic)
                return true;

            // Static properties don't require an instance
            PropertyInfo property = memberExpr.Member as PropertyInfo;
            if (property != null)
            {
                MethodInfo getter = property.GetGetMethod();
                if (getter != null && getter.IsStatic)
                    return true;
            }
            obj = memberExpr.Expression;
        }
        else
        {
            // Static methods don't require an instance
            if (callExpr.Method.IsStatic)
                return true;

            obj = callExpr.Object;
        }

        // Value types can't be null
        if (obj.Type.IsValueType)
            return true;

        // Instance member access or instance method call is not safe
        nullableObject = obj;
        return false;
    }
    return true;
}

Upvotes: 1

Bryn Keller
Bryn Keller

Reputation: 989

There's not really any good syntax for this. The coalesce operator is part of it, but you need to handle traversing through a null, not just replacing a null. One thing you could do would be to have a static "null object" for the class, something like:

public class Address 
{
  public static Address Null = new Address();
  // Rest of the class goes here
}

Then you could use the coalesce operator like so:

(person.MailingAddress ?? Address.Null).Street

If you want to go the extension method route, you could do something like this:

public static class NullExtension
{
    public static T OrNew<T>(this T thing)
        where T: class, new()
    {
        return thing ?? new T();
    }
}

Then you could do:

(person.MailingAddress.OrNew().Street)

Upvotes: 3

Reza ArabQaeni
Reza ArabQaeni

Reputation: 4907

You can use this Extension method:

public static TResult Maybe<TInput, TResult>(this TInput value, Func<TInput, TResult> evaluator, TResult failureValue)
            where TInput : class
        {
            return (value != null) ? evaluator(value) : failureValue;
        }

Example:

person.MailingAddress.MayBe(p=>p.Street,default(Street))

Upvotes: 0

Adam Rackis
Adam Rackis

Reputation: 83356

This code is technically a violation of the Law of Demeter, so some would consider it bad form to write this in the first place.

So no, there's no native syntax to accomplish what you want, but moving this code to a property in your Person class would make this calling code more clean, and also bring you in line with the law of demeter.

public string StreetAddress{
   get { return this.MailingAddress == null ? 
                   (String)null : person.MailingAddress.Street; }
}

Upvotes: 4

Related Questions