问题描述
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()
,我使用完成了任务
Seq.tryFind()
返回Car option
。
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
(如果要使用Seq.tryFind
,则使用IEnumerable
),它会自动返回一个选项,而您不会不必在您的F#代码中突然引入null
。这样,您的代码就会变得更加简单:
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)