Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

c#

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?

like image 525
Vu Nguyen Avatar asked Jan 10 '23 17:01

Vu Nguyen


1 Answers

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();
            }
        }
    }
}
like image 170
Marc Gravell Avatar answered Jan 23 '23 13:01

Marc Gravell