没有比无效更邪恶吗?

时间:2010-12-17 10:01:25

标签: f# null nullpointerexception

在F#中,它们没有空值并且不想支持它。程序员仍然必须为无类似于C#程序员制作的案例必须检查!= null。

没有人真的不如空虚吗?

4 个答案:

答案 0 :(得分:34)

null的问题在于您几乎可以无处不在地使用它,即引入无效状态,其中既不是也不是有道理

拥有'a option始终是一个明确的事情。您声明操作可以生成Some有意义的值或None,编译器可以强制执行以进行正确检查和处理。

通过劝阻null支持'a option类型,您基本上可以保证程序中的任何值都具有某种意义。如果某些代码设计为使用这些值,则不能简单地传递无效代码,如果存在option - 类型的函数,则 将涵盖所有可能性。

答案 1 :(得分:30)

当然它不那么邪恶了!

如果不检查None,那么在大多数情况下,您的应用程序中会出现类型错误,这意味着它将无法编译,因此它不会因NullReferenceException而崩溃(因为None转换为null)。

例如:

let myObject : option<_> = getObjectToUse() // you get a Some<'T>, added explicit typing for clarity
match myObject with
| Some o -> o.DoSomething()
| None -> ... // you have to explicitly handle this case

仍然可以实现类似C#的行为,但它不太直观,因为你必须明确地说“忽略这可以是无”:

let o = myObject.Value // throws NullReferenceException if myObject = None

在C#中,您不必将变量的大小写为null,因此您可能只是忘记进行检查。与上面相同的例子:

var myObject = GetObjectToUse(); // you get back a nullable type
myObject.DoSomething() // no type error, but a runtime error

编辑:Stephen Swensen是绝对正确的,我的示例代码有一些缺陷,正在匆忙写它。固定。谢谢!

答案 2 :(得分:14)

假设我向您展示了这样的函数定义:

val getPersonByName : (name : string) -> Person

当您传入数据存储中不存在的name人时,您认为会发生什么?

  • 该函数是否会抛出NotFound异常?
  • 是否返回null?
  • 如果不存在,是否会创建该人?

没有读取代码(如果你有权访问它),阅读文档(如果有人写得很好),或者只是调用函数,你就无从知晓了。这基本上是空值的问题:它们的外观和行为就像非空值一样,至少在运行时间之前。

现在假设你有一个带有这个签名的函数:

val getPersonByName : (name : string) -> option<Person>

这个定义非常清楚地说明了会发生什么:你要么让一个人回来,要么你不会,这种信息是在函数的数据类型中传达的。 通常,您可以更好地保证处理选项类型的情况而不是潜在的空值。

我会说选项类型比空值更仁慈。

答案 3 :(得分:3)

  

在F#中,它们没有空值并且不想支持它。程序员仍然必须为无类似于C#程序员制作的案例必须检查!= null。

     

没有人真的不如空虚吗?

尽管null每次在C#中取消引用对象时都会引入运行时错误(NullRefereceException)的潜在来源,None会强制您将运行时错误的来源显式化在F#。

例如,在给定对象上调用GetHashCode会导致C#静默注入运行时错误源:

class Foo {
  int m;
  Foo(int n) { m=n; }
  int Hash() { return m; }
  static int hash(Foo o) { return o.Hash(); }
};

相反,F#中的等效代码应该是null免费:

type Foo =
  { m: int }
  member foo.Hash() = foo.m

let hash (o: Foo) = o.Hash()

如果您真的想在F#中使用可选值,那么您将使用option类型,您必须明确处理它,否则编译器会发出警告或错误:

let maybeHash (o: Foo option) =
  match o with
  | None -> 0
  | Some o -> o.Hash()

你仍然可以通过规避类型系统(这是互操作所需的)来获得F#中的NullReferenceException

> hash (box null |> unbox);;
System.NullReferenceException: Object reference not set to an instance of an object.
   at Microsoft.FSharp.Core.LanguagePrimitives.IntrinsicFunctions.UnboxGeneric[T](Object source)
   at <StartupCode$FSI_0021>.$FSI_0021.main@()
Stopped due to error