C#优雅的方法来检查所有浅层属性是否为null(或任何属性不为null)

4
这个问题出现在我阅读这篇文章时: Deep Null checking, is there a better way? 以及这篇文章: C# elegant way to check if a property's property is null 假设我想检查所有属性都不为空,或者有任何一个属性不为空(浅层属性)。
搜索条件对象:
Keyword (Searches Name and Description) != null ||
SectorId != null ||
IndustryId != null ||
HasOption != null ||
HasFutures != null ||
30 properties to go...

我们可以看到,语法有些难以理解。我希望能够得到像这样的内容:

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

(如果我们希望所有上述属性都不为空)

或者

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

(如果我们想要任何属性不为null)
或者
SearchCriteria
.Has(criteria => criteria.Keywork).Or()
.Has(criteria => criteria.SectorId)
.Has(criteria => criteria.HasOption)
.etc

如果我们想要关键字部门ID有值HasOption有值。

那么我们是否有任何在codeplex上存在的项目?或者有没有优雅的方法可以结合深度空值检查和浅层空值检查


如果它是一个可平铺的“结构体”,您可以检查内存是否为零,尽管我不确定这是否算作“优雅”;它与C#相去甚远,比您所拥有的要丑陋得多。 - Jon Hanna
@JonHanna 这还取决于像 int foo 这样的字段,其值为 0 是否应该被视为 null - Marc Gravell
1
我建议您阅读这个链接... http://blogs.encodo.ch/news/view_article.php?id=167 优雅代码与干净代码"聪明是很好的,但这可能会严重限制愿意或能够处理该代码的人数。" - Paul Zahra
@MarcGravell,你可以检查涉及的字节子集,但这只会增加丑陋度。 - Jon Hanna
@PaulZahra:为这篇文章投一票,它非常有帮助。有时候代码的语义并不是我们所需要的。 - Vu Nguyen
@VuNguyen 实际上,我正在支持一个由两个拥有博士学位的开发人员创建的网站,其中一个去了微软工作,另一个去了谷歌...他们开发的代码虽然经常很聪明,有时也很高效,但通常是一个可怕的噩梦,难以维护/开发。 - Paul Zahra
2个回答

7
坦白地说,我建议你使用简单的版本,包括使用 ||&&== null!= null。它直接高效,并允许立即短路。如果你要做很多这样的操作,也许可以编写一个实用类,利用元编程(ExpressionILGenerator)为每个类型创建一个优化方法,检查所有属性,然后:
if(MyHelper.AllNull(obj)) ... // note this is probably actually generic

完整的示例:
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();
            }
        }
    }
}

我想知道在某些情况下,使用 |& 是否比使用 ||&& 更高效,因为避免分支的收益可能会超过短路的收益。在进行性能分析之前,默认情况下应该采用短路方法,因为它在99%的情况下更有效,但考虑这一点是很有趣的。 - Jon Hanna
@MarcGravell:我对你的回答很感兴趣。然而,我更喜欢使用上面最后一个伪代码中更灵活的语法来仅检查指定的属性。我同意“它是直接和高效的,并且允许立即短路。” - Vu Nguyen

2

虽然我支持Marc Gravell的回答,建议您完全写出来,但如果您坚持不重复使用!= null,则无需创建任何自定义方法。LINQ方法已经可以实现您想要的功能,例如:

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

为了涵盖 SearchCriteria 可能为 null 的情况,您可以使用:
new[] { SearchCriteria }.Where(criteria => criteria != null).SelectMany(...

这对于一个简单的检查来说是很多的分配...而且它依赖于您记得在多个地方维护代码。 - Marc Gravell
@MarcGravell 同意有很多分配,但是我阅读问题的方式,OP并不一定对检查所有属性感兴趣,而只对指定的属性感兴趣。 OP建议的语法也明确地写出了属性。 - user743382

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接