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

问题描述:

这种担忧出现,当我读到这个 Deep Null checking, is there a better way?C#优雅的方式来检查所有浅属性为null(或任何属性不为null)

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

说我想要检查所有属性是否为空或者是否有任何属性不为空。(浅属性)

SearchCriteria对象:

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 

(在情况下,我们希望所有以上性能NOT NULL)

OR

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 

(如果我们想要关键字 SectorId的值为 HasOption有价值。

那么,我们有这个项目Codeplex吗?或者可以结合使用深空检查和浅空检查的任何优雅方法?

+0

如果这是一个blittable'struct'你可以检查内存是否归零,但我不确定这是否是“优雅”。它已经从C#中移除了足够多,比您拥有的更加丑陋。 – 2014-09-30 10:29:59

+0

@JonHanna它也取决于像'int foo'这样的值为'0'的字段是否应该为'null' – 2014-09-30 10:35:16

+1

我建议你阅读这个... http://blogs.encodo.ch/news/ view_article.php?id = 167优雅的代码和干净的代码“聪明是美好的,但是你可能最终严重限制了愿意或能够使用该代码的人数。” – 2014-09-30 10:35:48

坦率地说,我会坚持简单的版本,涉及||&&,== 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(); 
      } 
     } 
    } 
} 
+0

我不知道这是否有时可能是使用'|'和'&实际上比'||'和'&&更有效的情况之一,因为避免分支的收益可以击败短循环的收益。在分析之前,短路方法是默认做的,否则99%的时间效率更高,但考虑很有趣。 – 2014-09-30 10:42:55

+0

@MarcGravell:我对你的回答感兴趣。不过,我更喜欢用更灵活的语法作为上面的最后一个伪代码来检查指定的属性。我同意“它直接,高效,并允许立即短路”。 – 2014-10-01 01:31:08

虽然我不支持马克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(... 
+0

这是一个简单的检查分配很多...它依赖于你记得在多个地方维护代码 – 2014-09-30 10:32:46

+0

@MarcGravell同意大量的分配,但我正在阅读这个问题的方式,OP不是'对检查*所有*属性一定感兴趣,但只有指定的属性。 OP的建议语法也明确写出了这些属性。 – hvd 2014-09-30 10:34:28