检查对象的每个属性是否为null

22

我有一个具有多个属性的类;

public class Employee
{
    public string TYPE { get; set; }
    public int? SOURCE_ID { get; set; }
    public string FIRST_NAME { get; set; }        
    public string LAST_NAME { get; set; }

    public List<Department> departmentList { get; set; }
    public List<Address> addressList { get; set; }

}

有时这个对象会返回一个带有任何属性值的值

Employee emp = new Employee();
emp.FIRST_NAME= 'abc';

剩余的值为空,这没问题。

但是,当对象属性中所有值都为空时,我应该如何检查?

就像string.IsNullOrEmpty()一样,适用于对象吗?

目前,我是这样检查的:

if(emp.FIRST_NAME == null && emp.LAST_NAME == null && emp.TYPE == null && emp.departmentList == null ...)

13
你的对象有6个属性,其中一个甚至不可为空,这意味着还剩下5个属性。你最好检查每个属性是否为空,而不是使用一些基于反射的复杂场景。你现在的处理方式可以说是正确的方式。 - Jamiec
2
在你的类中创建一个函数来为你完成这个任务怎么样?这样你需要的时候就可以直接调用它。 - git_gud
1
你目前的检查既可靠又易读。即使只有一个巨大的条件,您可能希望将其拆分为多行(每行一个检查)。此外,几个版本之前,他们在C#6中添加了空值条件运算符。对于单行检查,这些可能是更好的选择。https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/null-conditional-operators - Christopher
像 string.IsNullOrEmpty() 适用于对象吗? 只是为了澄清可能会有的误解:一个为 null 的对象并不等同于所有属性都为 null 的对象。如果你不区分这两者,那么在讨论代码时就会面临很多误解。 - Flater
@Flater,关于这个问题有很多误解,你能推荐一些好的阅读材料吗? - Abdul
2个回答

28

编辑

这个答案最近得到了一些投票,因此我决定对它进行改进,添加简单的缓存,使得ArePropertiesNotNull不会每次调用时都检索属性,而是针对每种类型只检索一次。

public static class PropertyCache<T>
{
    private static readonly Lazy<IReadOnlyCollection<PropertyInfo>> publicPropertiesLazy
        = new Lazy<IReadOnlyCollection<PropertyInfo>>(() => typeof(T).GetProperties());

    public static IReadOnlyCollection<PropertyInfo> PublicProperties => PropertyCache<T>.publicPropertiesLazy.Value;
}

public static class Extensions
{
    public static bool ArePropertiesNotNull<T>(this T obj)
    {
        return PropertyCache<T>.PublicProperties.All(propertyInfo => propertyInfo.GetValue(obj) != null);
    }
}

(以下是旧答案。)


你可以按照Joel Harkes的建议使用反射,例如我组合了这个可重复使用的、准备好使用的扩展方法。
public static bool ArePropertiesNotNull<T>(this T obj)
{
    return typeof(T).GetProperties().All(propertyInfo => propertyInfo.GetValue(obj) != null);    
}

然后可以像这样调用{{it}}

var employee = new Employee();
bool areAllPropertiesNotNull = employee.ArePropertiesNotNull();

现在你可以检查areAllPropertiesNotNull标志,该标志指示所有属性是否都不为null。如果所有属性都不为null,则返回true,否则返回false


该方法的优点

  • 对于检查,属性类型是否可为空并不重要。
  • 由于上述方法是通用的,您可以将其用于任何想要的类型,并且不必为要检查的每种类型编写样板代码。
  • 如果稍后更改类,则更具有未来性。(ispiro指出)。

缺点

  • 反射可能会非常慢,在这种情况下,它肯定比您当前所做的编写显式代码要慢。使用简单的缓存(如Reginald Blue所建议的)将消除大部分开销。

在我看来,使用ArePropertiesNotNull可以减少开发时间和代码重复,因此可以忽略轻微的性能开销,但你的情况可能有所不同。


2
反射在您更改类并忘记更改测试方法的情况下也更具有未来可扩展性。 - ispiro
2
通常反射较慢的部分是试图找到所有属性(或其他内容)。如果这是需要经常执行的操作,将属性缓存到数组中会显著加快速度。 - Reginald Blue
@ThomasFlinkow 当然可以。 - ispiro
2
只有当所有属性都不是null时,All(propertyInfo => propertyInfo.GetValue(obj) != null)才会返回true。所有属性都为null的对象将产生与仅具有单个null属性(false)相同的结果(false),这不符合OP描述的测试目的。我认为你的意思是Any(propertyInfo => propertyInfo.GetValue(obj) != null)! All(propertyInfo => propertyInfo.GetValue(obj) == null)(注意第二个的反转)。 - Flater
@ThomasFlinkow 请将答案从.Any更改为.All,这样我就可以将其标记为已接受,并且我认为.GetValue()需要两个参数,即.GetValue(obj, null) - Abdul
@trighati 请查看已编辑的答案。虽然存在PropertyInfo.GetValue(object, object)重载,但我选择使用PropertyInfo.GetValue(object),这样我就不必传递 null。当然,您可以使用任何一个,完全取决于您,不会有任何区别。感谢您将其标记为已接受。 - Thomas Flinkow

9

你可以通过手动编写代码来检查每个属性(最佳选项),或者使用反射(在此处了解更多)。

Employee emp = new Employee();
var props = emp.GetType().GetProperties())
foreach(var prop in props) 
{
   if(prop.GetValue(foo, null) != null) return false;
}
return true;

以下是这里的示例。

请注意,int类型不能为null!它的默认值为0。因此最好检查prop == default(int)而不是== null

选项3

另一种选择是实现INotifyPropertyChanged

在更改时将布尔字段值isDirty设置为true,然后您只需要检查该值是否为true,即可知道是否已设置任何属性(即使使用null设置了属性)。

警告:此方法仍然可以使每个属性为null,但仅检查是否调用了setter(更改了值)。


他们有int?,而不是int - Nisarg Shah
@trighati 没问题,但是你可能会忘记将所有属性设为可为空。 - Joel Harkes
@JoelHarkes 诸如此类的东西? - Abdul
@trighati int 整型、十进制数组、浮点数、结构体等。 - Joel Harkes

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