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 option
Seq.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
(如果要使用IEnumerable
Seq.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)