30

在与 C# 库交互时,我发现自己想要 C# 的 null 合并运算符用于Nullable结构和引用类型。

是否可以在 F# 中使用一个内联适当if情况的重载运算符来近似这个?

4

3 回答 3

30

是的,使用在这个SO答案“ F# 中的重载运算符”中发现的一些小技巧。

在编译时,可以内联使用('a Nullable, 'a) ->'a('a when 'a:null, 'a) -> 'a单个运算符的正确重载。甚至('a option, 'a) -> 'a可以投入更多的灵活性。

为了提供更接近 c# 运算符的行为,我制作了默认参数'a Lazy,以便除非原始值为null.

例子:

let value = Something.PossiblyNullReturned()
            |?? lazy new SameType()

执行:

NullCoalesce.fs [要点]:

//https://gist.github.com/jbtule/8477768#file-nullcoalesce-fs
type NullCoalesce =  

    static member Coalesce(a: 'a option, b: 'a Lazy) = 
        match a with 
        | Some a -> a 
        | _ -> b.Value

    static member Coalesce(a: 'a Nullable, b: 'a Lazy) = 
        if a.HasValue then a.Value
        else b.Value

    static member Coalesce(a: 'a when 'a:null, b: 'a Lazy) = 
        match a with 
        | null -> b.Value 
        | _ -> a

let inline nullCoalesceHelper< ^t, ^a, ^b, ^c when (^t or ^a) : (static member Coalesce : ^a * ^b -> ^c)> a b = 
        // calling the statically inferred member
        ((^t or ^a) : (static member Coalesce : ^a * ^b -> ^c) (a, b))

let inline (|??) a b = nullCoalesceHelper<NullCoalesce, _, _, _> a b

或者,我创建了一个库,它利用这种技术以及计算表达式来处理 Null/Option/Nullables,称为FSharp.Interop.NullOptAble

|?->改为使用运算符。

于 2014-01-17T19:46:55.743 回答
5

修改了jbtule接受的答案以支持 DBNull:

//https://gist.github.com/tallpeak/7b8beacc8c273acecb5e
open System

let inline isNull value = obj.ReferenceEquals(value, null)
let inline isDBNull value = obj.ReferenceEquals(value, DBNull.Value)

type NullCoalesce =
    static member Coalesce(a: 'a option, b: 'a Lazy) = match a with Some a -> a | _ -> b.Value
    static member Coalesce(a: 'a Nullable, b: 'a Lazy) = if a.HasValue then a.Value else b.Value
    //static member Coalesce(a: 'a when 'a:null, b: 'a Lazy) = match a with null -> b.Value | _ -> a // overridden, so removed
    static member Coalesce(a: DBNull, b: 'b Lazy) = b.Value //added to support DBNull
    // The following line overrides the definition for "'a when 'a:null"
    static member Coalesce(a: obj, b: 'b Lazy) = if isDBNull a || isNull a then b.Value else a // support box DBNull
let inline nullCoalesceHelper< ^t, ^a, ^b, ^c when (^t or ^a) : (static member Coalesce : ^a * ^b -> ^c)> a b = 
                                            ((^t or ^a) : (static member Coalesce : ^a * ^b -> ^c) (a, b))

用法:

let inline (|??) a b = nullCoalesceHelper<NullCoalesce, _, _, _> a b
let o = box null
let x = o |?? lazy (box 2)
let y = (DBNull.Value) |?? lazy (box 3)
let z = box (DBNull.Value) |?? lazy (box 4)
let a = None |?? lazy (box 5)
let b = box None |?? lazy (box 6)
let c = (Nullable<int>() ) |?? lazy (7)
let d = box (Nullable<int>() ) |?? lazy (box 8)
于 2015-10-20T21:22:12.817 回答
0

我通常defaultArg用于此目的,因为它是语言内置的。

于 2020-09-17T09:09:43.877 回答