在F#中,它们没有空值并且不想支持它。程序员仍然必须为无类似于C#程序员制作的案例必须检查!= null。
没有人真的不如空虚吗?
答案 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
人时,您认为会发生什么?
没有读取代码(如果你有权访问它),阅读文档(如果有人写得很好),或者只是调用函数,你就无从知晓了。这基本上是空值的问题:它们的外观和行为就像非空值一样,至少在运行时间之前。
现在假设你有一个带有这个签名的函数:
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