一起谈.NET技术,.Net令人纠结的Null

简介:   从我们刚学.Net编程起,我们的程序不断被从天而降NullReferenceException打断。直到今天,我们仍然时常为C#的Null或者VB的Nothing困惑。什么情况下我们该返回null,如果参数是null代表什么。

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

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

 
 
< configuration >
< configSections >
< section name = " reviewPeriod " type = " WordPadTest.ReviewPeriod,WordPadTest " />
</ configSections >
< reviewPeriod >
< Periods >
< 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 Framework对null使用上都有如此的问题,我们写程序时,应该更小心翼翼,对付这个难以捉摸、令人纠结的null。

目录
相关文章
|
5天前
|
机器学习/深度学习 人工智能 Cloud Native
在数字化时代,.NET 技术凭借其跨平台兼容性、丰富的类库和工具集以及卓越的性能与效率,成为软件开发的重要平台
在数字化时代,.NET 技术凭借其跨平台兼容性、丰富的类库和工具集以及卓越的性能与效率,成为软件开发的重要平台。本文深入解析 .NET 的核心优势,探讨其在企业级应用、Web 开发及移动应用等领域的应用案例,并展望未来在人工智能、云原生等方面的发展趋势。
15 3
|
5天前
|
开发框架 安全 Java
.NET技术的独特魅力与优势,涵盖高效的开发体验、强大的性能表现、高度的可扩展性及丰富的生态系统等方面,展示了其在软件开发领域的核心竞争力
本文深入探讨了.NET技术的独特魅力与优势,涵盖高效的开发体验、强大的性能表现、高度的可扩展性及丰富的生态系统等方面,展示了其在软件开发领域的核心竞争力。.NET不仅支持跨平台开发,具备出色的安全性和稳定性,还能与多种技术无缝集成,为企业级应用提供全面支持。
13 3
|
9天前
|
人工智能 开发框架 前端开发
C#/.NET/.NET Core技术前沿周刊 | 第 12 期(2024年11.01-11.10)
C#/.NET/.NET Core技术前沿周刊 | 第 12 期(2024年11.01-11.10)
|
8天前
|
人工智能 开发框架 安全
C#/.NET/.NET Core技术前沿周刊 | 第 13 期(2024年11.11-11.17)
C#/.NET/.NET Core技术前沿周刊 | 第 13 期(2024年11.11-11.17)
|
1月前
|
人工智能 开发框架 C#
C#/.NET/.NET Core技术前沿周刊 | 第 6 期(2024年9.16-9.22)
C#/.NET/.NET Core技术前沿周刊 | 第 6 期(2024年9.16-9.22)
|
1月前
|
人工智能 开发框架 Cloud Native
C#/.NET/.NET Core技术前沿周刊 | 第 9 期(2024年10.07-10.13)
C#/.NET/.NET Core技术前沿周刊 | 第 9 期(2024年10.07-10.13)
|
1月前
|
数据可视化 NoSQL C#
C#/.NET/.NET Core技术前沿周刊 | 第 8 期(2024年10.01-10.06)
C#/.NET/.NET Core技术前沿周刊 | 第 8 期(2024年10.01-10.06)
|
1月前
|
设计模式 开发框架 C#
C#/.NET/.NET Core技术前沿周刊 | 第 4 期(2024年9.1-9.8)
C#/.NET/.NET Core技术前沿周刊 | 第 4 期(2024年9.1-9.8)
|
2月前
|
人工智能 前端开发 开发工具
解读.NET 技术的开发潜力
本文全面介绍了.NET技术在软件开发领域的核心优势、创新应用及面临的挑战。.NET以其统一的开发平台、强大的工具和跨平台能力,成为企业级应用、Web应用乃至游戏开发的理想选择。然而,在性能优化、容器化及AI集成等方面仍需不断突破。通过积极拥抱开源和社区驱动模式,.NET将持续推动软件开发的进步。
58 1
|
2月前
|
人工智能 前端开发 Devops
.NET技术自发布以来,在软件开发领域发挥了重要作用
【9月更文挑战第12天】.NET技术自发布以来,在软件开发领域发挥了重要作用。本文分为三部分探讨其在现代开发中的应用:首先介绍.NET的核心价值,包括语言多样性、强大的开发工具支持、丰富的类库、跨平台能力和活跃的社区;接着分析其在企业级应用、Web开发、移动应用、云服务及游戏开发中的实际应用;最后讨论.NET面临的挑战与未来趋势,如性能优化、容器化、AI集成及跨平台框架竞争等。通过不断的技术创新和社区驱动,.NET将持续推动软件开发的进步。
45 4

热门文章

最新文章

下一篇
无影云桌面