我一直听到人们谈论非可空引用类型将如何解决如此多的错误并使编程变得更加容易.即使是null的创建者也称它为十亿美元的错误,而Spec#引入了非可空类型来解决这个问题.
编辑:忽略我对Spec#的评论.我误解了它是如何工作的.
编辑2:我必须和错误的人交谈,我真的希望有人能够与之争辩:-)
所以我猜想,在少数人中,我错了,但我不明白为什么这场辩论有任何优点.我认为null是一个bug查找工具.考虑以下:
class Class { ... } void main() { Class c = nullptr; // ... ... ... code ... for(int i = 0; i < c.count; ++i) { ... } }
BAM!访问冲突.有人忘了初始化c
.
现在考虑一下:
class Class { ... } void main() { Class c = new Class(); // set to new Class() by default // ... ... ... code ... for(int i = 0; i < c.count; ++i) { ... } }
哎呦.静默地跳过循环.追踪问题可能需要一段时间.
如果你的类是空的,那么代码无论如何都会失败.为什么不让系统告诉你(尽管有点粗鲁)而不是自己想出来?
有点奇怪的是,在这个帖子中标记为"回答"的响应实际上首先突出了null的问题,即:
我还发现我的大多数NULL指针错误都围绕着忘记检查string.h函数返回的函数,其中NULL用作指示符.
如果编译器可以在编译时捕获这些类型的错误而不是运行时,那不是很好吗?
如果您使用了类似ML的语言(某种程度上是SML,OCaml,SML和F#)或Haskell,则引用类型是不可为空的.相反,您通过将其包装为选项类型来表示"null"值.这样,如果函数的返回类型可以返回null作为合法值,则实际更改它的返回类型.所以,假设我想将用户拉出数据库:
let findUser username = let recordset = executeQuery("select * from users where username = @username") if recordset.getCount() > 0 then let user = initUser(recordset) Some(user) else None
查找用户具有类型val findUser : string -> user option
,因此函数的返回类型实际上告诉您它可以返回空值.要使用代码,您需要处理Some和None情况:
match findUser "Juliet Thunderwitch" with | Some x -> print_endline "Juliet exists in database" | None -> print_endline "Juliet not in database"
如果你不处理这两种情况,代码甚至都不会编译.因此类型系统保证您永远不会得到空引用异常,并且它保证您始终处理空值.如果函数返回user
,则保证它是对象的实际实例.迷死人.
现在我们在OP的示例代码中看到问题:
class Class { ... } void main() { Class c = new Class(); // set to new Class() by default // ... ... ... code ... for(int i = 0; i < c.count; ++i) { ... } }
初始化和未初始化的对象具有相同的数据类型,您无法区分它们之间的区别.偶尔,null对象模式可能很有用,但上面的代码表明编译器无法确定您是否正确使用了类型.
我不明白你的例子.如果你的"= new Class()"只是一个占位符而不是null,那么它(在我看来)显然是一个bug.如果不是,那么真正的错误是"......"没有正确设置其内容,这在两种情况下完全相同.
一个例外,它向您显示您忘记初始化c将告诉您它何时未初始化,而不是它应该初始化的位置.类似地,错过的循环将(隐式)告诉您需要具有非零.count的位置,而不是应该执行的操作或位置.我没有看到任何一个对程序员来说更容易.
我不认为"无空"的意思是简单地进行文本查找和替换,并将它们全部放入空实例中.那显然没用.关键是要构造你的代码,这样你的变量永远不会处于无用/不正确的值,其中NULL只是最常见的.