如果C#对于引用类型具有null的概念,对于结构具有Nullabe的概念。 这可以采用以下两种形式之一(为演示起见,我在这里使用int类型,但该类型可以是任何结构)。
免责声明 Nullabe Nullable<T> , - ,
它们都是等效的。
Nullable类提供了一些帮助程序属性和方法,这些属性和方法使使用null类型和结构更容易。 这些是以下内容:
- Hasvalue
- 价值
- GetValueOrDefault()
- GetValueOrDefault(T)
Option类型的形式与F#稍有不同,后者是一种更为F#友好的类型,它不喜欢处理null,而是喜欢用“可能包含类型值”或“可能没有值。” 听起来像Nullable,但最后它是F#类型,因此可以期望它在F#事物中正常使用。
Option F#类型的另一点值得注意的是,它可以用于任何类型,而不仅仅是结构。 这不同于.NET Nullable,后者只能与结构一起使用。
无值时使用值
None ; 否则,表达式
Some (...)会分配一个值。 与模式匹配时可以使用值Some和None,我们将在本文中看到一个示例。
像Nullable一样,F#Option类型具有几种辅助属性/方法,如下表所示。
无'T选项一个静态属性,使您可以创建值为None的参数值。
伊斯通布尔如果参数为None,则返回true。
有点布尔如果参数的值不是None,则返回true。
一些
'T选项创建值不为None的参数的静态成员。
价值'T
返回一个基本值,或者如果该值为None则抛出NullReferenceException。
创建选项
因此,既然我们知道什么是期权类型,我们如何创建它们。 让我们看一些例子。 请注意,在此示例中,我使用了辅助方法IsSome / IsNone。 我个人认为,与样本进行比较是最好的方法,因为它将帮助您比较所有情况,包括“否”选项。
实际上,我将向您展示如果您决定使用辅助方法,那么在使用Option类型工作时出错就容易了,但首先让我们看一个正确的例子。
let someInt = Some(43) let someString = Some("cat") let printTheOption (opt :Option<'a>) = printfn "Actual Option=%A" opt printfn "IsNone=%b, IsSome=%b Value=%A\r\n\r\n" opt.IsNone opt.IsSome opt.Value printfn "let someInt = Some(43)" printfn "let someString = Some(\"cat\")" printfn "printTheOption (someInt)" printTheOption someInt printfn "printTheOption (someString)" printTheOption someString

但是,如果我们使用此代码再次尝试该操作,那么对于Option值将没有None,我们将其传递给printTheOption函数:
let demoNone = None let printTheOption (opt :Option<'a>) = printfn "Actual Option=%A" opt printfn "IsNone=%b, IsSome=%b Value=%A\r\n\r\n" opt.IsNone opt.IsSome opt.Value printfn "let demoNone = None" printfn "printTheOption demoNone" printTheOption demoNone

如您所见,我们在这里遇到了问题。 问题是我们试图使用辅助属性Option.Value获取Option值,在这种情况下,它为None,因此我们得到了NullReferenceException。 上表显示,当您使用辅助属性和方法时,可能会遇到异常。 好吧,您可以使用IsNone方法,并且当您只可以使用一个很好的干净模式匹配时,您将始终使用此方法检查值。
如果您不能接受,请自问使用C#时必须检查空值多少次。 这甚至导致人们在常规.NET代码中包含功能构造,例如Maybe Null Monad。
因此,既然我们已经看到了使用辅助方法/属性的危险,那么现在让我们将注意力转向如何避免这些异常:
et printTheOption (opt :Option<'a>) = match opt with | Some a -> printfn "opt is Some, and has value %A" a | None -> printfn "opt is None" let demoNone = None let someInt = Some 1 let someString = Some "crazy dude in the monkey house" printTheOption demoNone printTheOption someInt printTheOption someString
我个人的观点是,它比IsSome / IsNone点缀的代码更具可读性。 当然,这是每个人的事,但是我们在此简单功能中涵盖了所有基础知识这一事实不容忽视。

期权vs空
因此,与Nullabe相比,我们讨论了F#中的Option,并且我们知道Option类型可以与任何类型一起使用,而Nullable仅可以与结构一起使用。 但是,与.NET中的常规引用类型相比,选项类型呢? 好吧,Option的一大优势在于,当您在.NET中使用引用类型时,您正在处理的是指针引用,因此可以将其设置为null。 但是,对象的类型与声明的类型相同,可能在堆(Heap)中包含对对象的有效引用,也可以为null。
这样写是正常的:
string s1 = "cats"; int s1Length = s1.Length; string s2 = null; int s2Length = s2.Length;
这将成功编译。 但是,当我们运行此代码时,我们将获得NullReferenceException,为此我们将被迫退出以保护所有代码以免可能出现null。 即使您有一个不错的保护类可以检查该值并对其进行处理/抛出更有意义的异常,这也会非常繁琐。
此屏幕快照使用LinqPad,因此,如果您以前从未看过LinqPad,这似乎有点不寻常,但是请相信我,在不同的IDE中仍然可以得到相同的结果。

现在让我们看看F#中的等效代码看起来如何
let s1 = "Cats" let s1Length = s1.Length; let s2 = None let s2Length = s2.Length; //excplicily string typed None let s3 = Option.None let s3Length = s3.Length;
在此代码上,您将立即得到编译错误,因为s3将被视为另一种没有“ Length”属性的类型。
选项比较
期权类型被认为是相等的,它们具有相同的类型,并且它们所包含的类型是相等的,因此遵守持有类型的相等性规则。
因此,这种情况会导致F#中立即出现编译时错误
let o1 = Some 1 let o2 = Some "Cats" printfn "o1=o2 : %b" (o1 = o2)

由于类型相同,这将按预期工作
let o1 = Some "Cats" let o2 = Some "Cats" let o3 = Some "cats" printfn "o1=o2 : %b" (o1 = o2) printfn "o2=o3 : %b" (o2 = o3)
