F#9:类型选项

如果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) 

图片

Source: https://habr.com/ru/post/zh-CN470918/


All Articles