• .Net令人纠结的Nu“.NET研究”ll 狼人:


      从我们刚学.Net编程起,我们的程序不断被从天而降NullReferenceException打断。直到今天,我们仍然时常为C#的Null或者VB的Nothing困惑。什么情况下我们该返回null,如果参数是null代表什么。许多类型,有两种不同意义的空状态,一种是null,一种是其本身或其某个属性集合中没有元素,这就更容易产生误用。常听有人说,Null这个概念在编程语言中根本不应该存在。但是,从C++到Java到.Net,它从未离开过。

      最近,注意到.Net Framework在读取程序配置文件的一个小Bug。比如我在配置文件中,自定义了名为ReviewPeriod的节点:

    <configuration>
    <configSections>
    <section name=上海徐汇企业网站设计与制作"color: #800000;">"reviewPeriod" type="WordPadTest.ReviewPeriod,WordPadTest"/>
    </configSections>
    <reviewPeriod>
    <Periods>
    <上海闵行企业网站设计与制作 style="color: #000000;">period id="1" />
    <period id="2" name="d" />
    <period id="3" name="m" />
    </Periods>
    </reviewPeriod>
    </configuration>

      注意第一个period节点没有定义name属性。.Net Framework中关于程序配置处理的类都在System.Configuration命名空间下,应用很广泛,比如Asp.Net和WCF项目,还有许多第三方框架如NHibernate。下面定义三个类,分别描述自定义节点配置,自定义节点集合,自定义节点实体,这也是最普通的程序自定义配置处理方式。然后就可以遍历ConfigurationManager.GetSection("reviewPeriod") as ReviewPeriod 访问每个period对象。

    namespace WordPadTest
    {
    class ReviewPeriod : ConfigurationSection
    {
    [ConfigurationProperty(
    "Periods")]
    public Periods Periods
    {
    get { return this["Periods"] as Periods; }
    }
    }

    class Periods: ConfigurationElementCollection
    {
    protected override ConfigurationElement CreateNewElement()
    {
    return new Period();
    }

    protected override object GetElementKey(ConfigurationElement element)
    {
    return (element as Period).Id;
    }

    public override ConfigurationElementCollectionType CollectionType
    {
    get { return ConfigurationElementCollectionType.BasicMap; }
    }

    protected override string ElementName
    {
    get { return "period"; }
    }
    }

    public class Period:ConfigurationElement
    {
    [ConfigurationProperty(
    "name")]
    public上海企业网站制作 string Name
    {
    get { return (string)this["name"]; }
    }
    [ConfigurationProperty(
    "id")]
    public int Id
    {
    get { return (int)this["id"]; }
    }
    }
    }

      这段程序运行没问题,意外的是在第一个period节点上,它的Name属性是什么呢,一直以来我都弄错了,可能还有许多人也弄错了。正确答案是String.Empty,而不是null。而且即使我在ConfigurationProperty加上DefaultValue=null,Name返回的依然是String.Empty。

      在我看来,null应该用于表示一种在我们预料之外的状态,包括传入的参数和返回的结果。比如一段从数据库读取记录的代码,如果执行中出现异常,则返回null,和正常执行后未找到任何匹配记录的情况截然不同。同理,对于节点的name属性未定义的情况,显然也不同于定义了name=”"的情况,返回null是合理的。而ConfigurationElement不允许string属性为null,可能有某方面的考虑,但这应该是个Bug,因为它返回值含混不清,是对string空值的误解。

      请再来看另一个例子:如果这个返回值变成了参数的情况。在实际软件中,读取XML数据源时,通常要进行架构验证,比如像下面这样:

    XmlReaderSettings settings = new XmlReaderSettings();
    settings.Schemas.上海网站建设Add(targetNamespace,
    "postlist.xsd"); //targetNamespace是传入方法的参数
    settings.ValidationType = ValidationType.Schema;
    XmlReader reader
    = XmlReader.Create("postlist.xml", settings);

      这段代码有一个问题,它未判断targetNamespace的是Null还是String.Empty。对于XmlSchemaSet的Add方法,targetNamespace参数如果为null,则表示使用xsd文件中定义的命名空间,而如果传入String.Empty,XmlSchemaSet依然认为,是我们手动指定了一个空的命名空间,而这完全不是我们想要的。

      如果在xsd文件中定义了targetNamespace,我们手动指定的namespace必须与之一致,否则将抛出异常。而xsd文件中的targetNameSpace属性,你可以不加它,但加了就不允许空值。换句话说,我们传入的为String.Empty时targetNameSpace参数时,xsd中只要有targetNamespace的定义,程序必然出错。下图也是上面代码执行的结果,和传null值的情况有天壤之别。

    image

      我们不能怪罪写Xml验证的人太马虎,微软一个内部框架中就有这个问题。归根究底,是.Net Framework对null的误用,导致了我们的误解。

      首先,ConfigurationElement类对返回值的误用,属性值该是null却返回String.Empty。

      其次,XmlSchemaSet类的Add方法有一定问题,应该提供一个重载函数,不需要传入为null的targetNamespace参数,直接用xsd文件中的对应属性。 不但方便,也减少出错的机会。当然,它正确的区分了null和empty的含义,这点还是值得称赞的。

      连.Net Framew上海企业网站设计与制作ork对null使用上都有如此的问题,我们写程序时,应该更小心翼翼,对付这个难以捉摸、令人纠结的null。

    声明:此博有部分内容为转载,版权归原作者所有~
  • 相关阅读:
    .net开发微信公众号(2)-验证服务器地址
    [广度遍历和深度遍历]聊聊算法和设计模式
    DDD领域驱动设计之聚合、实体、值对象
    DDD领域驱动设计之运用层代码
    DDD领域驱动设计之领域服务
    DDD领域驱动设计之领域基础设施层
    DDD领域驱动设计实践篇之如何提取模型
    Docker最全教程——从理论到实战(七)
    Docker最全教程——从理论到实战(六)
    Docker最全教程——从理论到实战(五)
  • 原文地址:https://www.cnblogs.com/waw/p/2217015.html
Copyright © 2020-2023  润新知