用于检查对象层次结构中的空值的建议解决方案

时间:2010-03-03 09:50:23

标签: c#

重新审视上一篇文章的主题,我想对建议的解决方案提供一些反馈。

在我的代码(C#3.0)中,我需要处理来自大型反序列化XML文档的对象结构中的服务提供给我的应用程序表单。表单可以包含许多不同类型的数据,因此我需要进行大量的空检查以查看数据是否存在。

例如,可能有以下值:

ApplicationForm.EmployeeInfo.PersonalInfo.Name

要在这里检查非空值,我必须检查中途的每一步。

我想避免使用long if语句和try-catch块,所以我的解决方案是扩展方法

public static T NN<T>(this T obj) where T : class, new()
{
    return obj ?? new T();
}

可以用作

if (ApplicationForm.NN().EmployeeInfo.NN().PersonalInfo.NN().Name != null)

这样,如果未初始化层次结构的下一步,则暂时新建并返回。 NN(非Null)方法返回默认对象而不是null,在我的情况下非常适合。当使用xsd-tool生成的对象结构时,我需要检查所有可选元素的“~Propecified”属性,它就派上用场了。

现在我的问题是我是否遗漏了一些让这个好的解决方案成为糟糕选择的东西。是否有许多空的默认对象在内存或性能方面过于沉重,还是存在其他问题?

我做的一个快速但当然不具决定性的测试表明没有显着的性能影响。

4 个答案:

答案 0 :(得分:4)

问一个程序员她做了什么,你通常的回答是“嗯,编程?” 错。
我有一位CS教授为老板做了一段时间。他声称程序员编写代码的时间不到10%。大部分时间花在阅读调试代码上。因此,他坚持认为我们必须避免扭曲属性或函数调用的“链”。这与你的情况非常相关 - NN黑客可能看起来不错,今天可以节省几分钟,但将来会花费不少钱。

答案 1 :(得分:2)

我同意这个更长:

if (ApplicationForm != null && 
    ApplicationForm.EemployeeInfo != null && 
    ApplicationForm.EemployeeInfo.PersonalInfo != null &&
    ApplicationForm.EemployeeInfo.PersonalInfo.Name != null) {
    // ...
} 

但它也更具可读性;如果我要维护您的申请,我将非常感谢您以标准方式进行检查 我会追求可读性,即使代价较长,即使您的解决方案没有性能损失。

答案 2 :(得分:0)

几天前我写了一篇关于使用Linq表达式自动执行空值检查的方法的博文:

http://www.thomaslevesque.com/2010/02/21/automating-null-checks-with-linq-expressions/

我提出了一个解决方案,允许你写这样的东西:

string name = ApplicationForm.NullSafeEval(a => a.EmployeeInfo.PersonalInfo.Name);

分析并重写表达式树,以便为属性路径的每个级别添加空检查

然而,尽管此解决方案有效,但如果性能至关重要,我建议不要使用它...

答案 3 :(得分:0)

使用Null monad。只要您using它就可以在同一个文件或不同的文件中。

public static class NullMonad {
    public static TResult SelectMany<TIn, TOut, TResult>(this TIn @in, Func<TIn, TOut> remainder, Func<TIn, TOut, TResult> resultSelector)
        where TIn : class
        where TOut : class
        where TResult : class {
        var @out = @in != null ? remainder(@in) : null;
        return @out != null ? resultSelector(@in, @out) : null;
    }
}

然后你可以使用LINQ:

var salary = from form in applicationForm
             from info in form.employeeInfo
             from cond in info.workingConditions
             select cond.salary

如果工资存在,则返回工资;如果任何先前的工作结果为null,则返回null,不抛出异常。它减少了手动检查每个变量的重复次数。它还避免了创建所有未使用的&#34; NN&#34;对象。