F# |如何在匹配表达式中管理 null(不可为 null)?


open System.Linq
type Car(model:string, color:string) =
member this.Model = model
member this.Color = color

member this.ToString() = sprintf "ModeL:%s Color:%s" model color

let cars = [
Car("Ferrari", "red")
Car("BMW", "blu")
]
let getCar model = 
match cars.FirstOrDefault(fun c -> c.Model = model) with
| car -> Some(car)                       // matches ALWAYS !
//| car when car <> null -> Some(car)
//| car when car <> (default(Object)) -> Some(car)
//| null -> None
//| Null -> None     
let mercedes = getCar("Mercedes")
let car = match mercedes with
| Some c -> c.ToString()           // c is null !!!
| _ -> "not found"

FirstOrDefault不返回 Nullabe,所以我无法与null.
匹配,那么,如何在 Match 表达式中检查从函数返回的null

我正在使用FirstOrDefault,因为我尝试使用Enumerable.
中最简单的对象(Seq),我知道我可以使用从Enumerable开始的其他东西,但我仍然喜欢了解我在这里缺少什么。

[解决方案]

感谢@Abel使用.tryFind()的建议,我使用 返回Car optionSeq.tryFind().

let getCar model = 
let cars = lazy(
// this.Collection.Indexes.List().ToEnumerable()   // this is the real data I'm using (MongoDB indexes of a collection)
// |> Seq.map parseIndex  // a function that create Car (Index) from the BsonDocumentBsonDocument
cars.AsEnumerable()        
)
cars.Value |> Seq.tryFind(fun c -> c.Model = model)

let mercedes = match getCar("Mercedes") with
| Some c -> c.ToString()
| _ -> "not found"

let ferrari = match getCar("Ferrari") with
| Some c -> c.ToString()
| _ -> "not found"   

F#中的类不能将null作为正确的值(它是 F# 更强大的方面之一)。但是,您可以通过添加AllowNullLiteral属性来破坏此协定:

[<AllowNullLiteral>]
type Car(model:string, color:string) =
member this.Model = model
member this.Color = color

现在您可以创建null的实例,当您需要与可以返回null的代码互操作时,在代码中使用它会更容易。

请注意,带有| car ->的代码是一个变量模式,这意味着它会捕获所有内容并将值分配给变量car。不确定你想在这里做什么,但是类上的模式匹配不是很有用。

如果您需要匹配null,请将其作为第一个匹配项,第二个匹配项可以car,捕获其他所有内容。然后,您的代码将变为:

[<AllowNullLiteral>]
type Car(model:string, color:string) =
member this.Model = model
member this.Color = color

member this.ToString() = sprintf "ModeL:%s Color:%s" model color

module X = 
let cars = [
Car("Ferrari", "red")
Car("BMW", "blu")
]
let getCar model = 
match cars.FirstOrDefault(fun c -> c.Model = model) with
| null -> None
| car -> Some(car)      // matches everything else

关于代码的另一个注意事项:Car类型也可以创建为记录:

type Car =
{ 
Model: string
Color: string
}

而不是使用 LINQ,更习惯用List.tryFind(如果要使用IEnumerableSeq.tryFind),它会自动返回一个选项,并且您不必突然在 F# 代码中引入null。然后,您的代码 woule 总体上变得简单得多:

type Car =
{ 
Model: string
Color: string
}

override this.ToString() = sprintf "ModeL:%s Color:%s" this.Model this.Color

module X = 
let cars = [
{ Model = "Ferrari"; Color = "red" }
{ Model = "BMW"; Color = "blu" }
]
let getCar model = cars |> List.tryFind (fun x -> x.Model = model)

相关内容

  • 没有找到相关文章