Deep null checking, is there a better way?

2019-01-01 08:35发布

Note: This question was asked before the introduction of the .? operator in C# 6 / Visual Studio 2015.

We've all been there, we have some deep property like cake.frosting.berries.loader that we need to check if it's null so there's no exception. The way to do is is to use a short-circuiting if statement

if (cake != null && cake.frosting != null && cake.frosting.berries != null) ...

This is not exactly elegant, and there should perhaps be an easier way to check the entire chain and see if it comes up against a null variable/property.

Is it possible using some extension method or would it be a language feature, or is it just a bad idea?

标签: c# null
16条回答
看淡一切
2楼-- · 2019-01-01 08:42

Where you need to achieve this, do this:

Usage

Color color = someOrder.ComplexGet(x => x.Customer.LastOrder.Product.Color);

or

Color color = Complex.Get(() => someOrder.Customer.LastOrder.Product.Color);

Helper class implementation

public static class Complex
{
    public static T1 ComplexGet<T1, T2>(this T2 root, Func<T2, T1> func)
    {
        return Get(() => func(root));
    }

    public static T Get<T>(Func<T> func)
    {
        try
        {
            return func();
        }
        catch (Exception)
        {
            return default(T);
        }
    }
}
查看更多
无与为乐者.
3楼-- · 2019-01-01 08:47

I've found this extension to be quite useful for deep nesting scenarios.

public static R Coal<T, R>(this T obj, Func<T, R> f)
    where T : class
{
    return obj != null ? f(obj) : default(R);
}

It's an idea I derrived from the null coalescing operator in C# and T-SQL. The nice thing is that the return type is always the return type of the inner property.

That way you can do this:

var berries = cake.Coal(x => x.frosting).Coal(x => x.berries);

...or a slight variation of the above:

var berries = cake.Coal(x => x.frosting, x => x.berries);

It's not the best syntax I know, but it does work.

查看更多
时光乱了年华
4楼-- · 2019-01-01 08:48

I like approach taken by Objective-C:

"The Objective-C language takes another approach to this problem and does not invoke methods on nil but instead returns nil for all such invocations."

if (cake.frosting.berries != null) 
{
    var str = cake.frosting.berries...;
}
查看更多
回忆,回不去的记忆
5楼-- · 2019-01-01 08:50

Besides violating the Law of Demeter, as Mehrdad Afshari has already pointed out, it seems to me you need "deep null checking" for decision logic.

This is most often the case when you want to replace empty objects with default values. In this case you should consider implementing the Null Object Pattern. It acts as a stand-in for a real object, providing default values and "non-action" methods.

查看更多
裙下三千臣
6楼-- · 2019-01-01 08:50

There is Maybe codeplex project that Implements Maybe or IfNotNull using lambdas for deep expressions in C#

Example of use:

int? CityId= employee.Maybe(e=>e.Person.Address.City);

The link was suggested in a similar question How to check for nulls in a deep lambda expression?

查看更多
回忆,回不去的记忆
7楼-- · 2019-01-01 08:51

Try this code:

    /// <summary>
    /// check deep property
    /// </summary>
    /// <param name="obj">instance</param>
    /// <param name="property">deep property not include instance name example "A.B.C.D.E"</param>
    /// <returns>if null return true else return false</returns>
    public static bool IsNull(this object obj, string property)
    {
        if (string.IsNullOrEmpty(property) || string.IsNullOrEmpty(property.Trim())) throw new Exception("Parameter : property is empty");
        if (obj != null)
        {
            string[] deep = property.Split('.');
            object instance = obj;
            Type objType = instance.GetType();
            PropertyInfo propertyInfo;
            foreach (string p in deep)
            {
                propertyInfo = objType.GetProperty(p);
                if (propertyInfo == null) throw new Exception("No property : " + p);
                instance = propertyInfo.GetValue(instance, null);
                if (instance != null)
                    objType = instance.GetType();
                else
                    return true;
            }
            return false;
        }
        else
            return true;
    }
查看更多
登录 后发表回答