简体   繁体   中英

C# elegant way to check if all shallow properties are null (or any property is not null)

This concern comes up when I read this Deep Null checking, is there a better way?

and this

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

Say I want to check if all properties are NOT null or there is any property is NOT null. (shallow properties)

SearchCriteria object:

Keyword (Searches Name and Description) != null ||
SectorId != null ||
IndustryId != null ||
HasOption != null ||
HasFutures != null ||
30 properties to go...

As we can see, the syntax is somehow hard to read. And I want something like

SearchCriteria
.Has(criteria => criteria.Keywork)
.Has(criteria => criteria.SectorId)
.Has(criteria => criteria.HasOption)
.etc

(in case we want all above properties are NOT null)

OR

SearchCriteria
.Has(criteria => criteria.Keywork).Or()
.Has(criteria => criteria.SectorId).Or()
.Has(criteria => criteria.HasOption).Or()
.etc

(In case we want any property is NOT null)

OR

SearchCriteria
.Has(criteria => criteria.Keywork).Or()
.Has(criteria => criteria.SectorId)
.Has(criteria => criteria.HasOption)
.etc

(In case we want Keyword or SectorId has value and HasOption has value.

So do we have any existed project on codeplex for this? Or any elegant approach that can combine both deep null checking and shallow null checking ?

Frankly, I'd stick with the simple version involving || or && , == null or != null . It it direct and efficient, and allows immediate short-circuiting. If you are going to be doing this lots , you could perhaps write a utility class that uses meta-programming ( Expression or ILGenerator maybe) to create an optimized method once per-type that checks all the properties, then:

if(MyHelper.AllNull(obj)) ... // note this is probably actually generic

Full example:

using System;
using System.Linq.Expressions;
using System.Reflection;

static class Program
{
    static void Main()
    {
        bool x = MyHelper.AnyNull(new Foo { }); // true
        bool y = MyHelper.AnyNull(new Foo { X = "" }); // false
    }
}
class Foo
{
    public string X { get; set; }
    public int Y { get; set; }
}
static class MyHelper
{
    public static bool AnyNull<T>(T obj)
    {
        return Cache<T>.AnyNull(obj);
    }
    static class Cache<T>
    {
        public static readonly Func<T, bool> AnyNull;

        static Cache()
        {
            var props = typeof(T).GetProperties(
                BindingFlags.Instance | BindingFlags.Public);

            var param = Expression.Parameter(typeof(T), "obj");
            Expression body = null;

            foreach(var prop in props)
            {
                if (!prop.CanRead) continue;

                if(prop.PropertyType.IsValueType)
                {
                    Type underlyingType = Nullable.GetUnderlyingType(
                                              prop.PropertyType);
                    if (underlyingType == null) continue; // cannot be null

                    // TODO: handle Nullable<T>
                }
                else
                {
                    Expression check = Expression.Equal(
                        Expression.Property(param, prop),
                        Expression.Constant(null, prop.PropertyType));
                    body = body == null ? check : Expression.OrElse(body, check);
                }
            }
            if (body == null) AnyNull = x => false; // or true?
            else
            {
                AnyNull = Expression.Lambda<Func<T, bool>>(body, param).Compile();
            }
        }
    }
}

Although I do support Marc Gravell's answer to simply write it out fully, if you do insist on not repeating the != null , you do not need to create any custom methods. LINQ methods can already do what you want, for example like so:

new[] { SearchCriteria }.SelectMany(criteria => new object[] {
  criteria.Keywork,
  criteria.SectorId,
  criteria.HasOption,
  ...
}).Any(p => p != null)

To also cover the case where SearchCriteria itself might be null , you can use

new[] { SearchCriteria }.Where(criteria => criteria != null).SelectMany(...

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM