I seem to have fallen into some massive, massive trouble with NullReferenceExceptions
最近,我开发了一个软件,可以解析和显示网站上的XML信息。很简单,对吧?
我正在获取大量nullreferenceexceptions。例如,此方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | private void SetUserFriends(List<Friend> list) { int x = 40; int y = 3; if (list != null) { foreach (Friend friend in list) { FriendControl control = new FriendControl(); control.ID = friend.ID; control.URL = friend.URL; control.SetID(friend.ID); control.SetName(friend.Name); control.SetImage(friend.Photo); control.Location = new Point(x, y); panel2.Controls.Add(control); y = y + control.Height + 4; } } } |
为了防止出现异常,我必须在实际的foreach循环周围包装一个难看得像罪恶的东西。
我觉得我只是把创可贴贴贴在一个漏气的轮胎上,而不是实际解决问题。我有办法解决这个问题吗?也许我应该读一本关于编程模式的书,或者什么?
真的,我迷路了。我可能问错了问题。
听起来,如果在方法中收到错误的参数,您可能不确定该怎么做。您现在所做的并没有本质上的错误,但是更常见的模式是检查方法头中的参数,如果这些参数不是您所期望的,则抛出异常:
1 2 3 4 |
这是一种常见的防御性编程模式——检查以确保提供的数据通过了基本的健全性检查。
现在,如果您自己显式地调用这个方法,并且您发现这个方法在不期望它时接收到一个空的
我可能会被"没有多个出口"的人群否决,但我通常会在方法的开头简单检查一下:
1 | if (list == null || list.Count == 0) return; |
这指定了退出条件,然后您就不需要担心方法中存在多个级别的缩进。只有当你能接受你的列表是空的或者是空的这一事实时,这才有效——这在某些情况下可能发生。
但我同意codeka,因为你需要看看调用代码,并找出是否可以从中改进它。
似乎防御性编程和参数验证是您要寻找的。
正如其他人所说,简单的参数验证可以为您工作:
1 2 |
或者,如果您厌倦了为每个参数不断地编写这样的检查,那么您可以签出许多开放源码.NET前提条件强制库中的一个。我喜欢刀刃。条件。
这样,您就可以使用如下内容:
1 | Condition.Requires(list,"list").IsNotNull(); |
但是,设置上述任何一种前提条件都只会指定您的方法不接受空值。您的问题仍然存在,因为您正在将空值传递给方法!要解决这个问题,您必须检查调用方法的是什么,并找出传入空对象的原因。
除了抛出argumentNullException异常之外,还有一个叫做"空obejct模式"的东西,如果您想传递一个空值,可以使用它来表示,例如,某个东西不存在,但不想显式地检查空值。本质上,它是一个实现相同接口的存根类,但它的方法通常是空的,或者返回的值足以使它们完成。http://en.wikipedia.org/wiki/null_对象_模式
对于不能轻易地表示不存在的值类型(不能为空)也很有用。
你的确问错了问题。正确的问题是"空值是否表示无效的输入或表示x的标志"。
在向语言中添加非空引用类型并使其成为各种API之前,您可以选择在代码中显式地使用该类型,或者让空引用异常帮助您找到违反期望的位置,然后以某种方式修复数据/代码。
如果输入无效,我将提前返回(或提前抛出InvalidArgumentException)。
例如:
1 2 3 4 5 6 7 | private void SetUserFriends(List<Friend> list) { if (list == null) return; /* Do stuff */ } |
或者,您可以使用常规的空合并模式:
1 2 3 4 5 6 | private void SetUserFriends(List<Friend> list) { list = list ?? new List<Friend>(); /* Do Stuff */ } |