F#中的空合并运算符?

Posted

技术标签:

【中文标题】F#中的空合并运算符?【英文标题】:Null Coalescing Operator in F#? 【发布时间】:2014-02-07 07:04:49 【问题描述】:

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

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

【问题讨论】:

不错的文章,还包括选项合并:troykershaw.com/blog/… @Giles 该博文大多已过时,它包括选项合并,其行为在 f# 中更简洁地表达为let (|?) = defaultArg 我还没有测试过,但文章确实建议使用let inline (|??) (a: 'a Nullable) b = if a.HasValue then a.Value else b 形式的空合并替换。我是 F# 的新手,所以我可能错了,但您的建议(可能带有参数?)不会导致始终使用默认值吗? 不,defaultArg 是一个内置函数,它的工作原理究竟如何|?在博客文章中进行了描述。您描述的可空版本具有仅使用 Nullable 结构而不是任何可能具有空值的类型的严重限制。但是这个问题是针对适用于 Options 或 Nullables 或其他变体的单个运算符,而不是 |?、|??、|???或添加另一个?对于每个略有不同的单子。只需一个合并运算符即可。 啊,我明白了 (msdn.microsoft.com/en-us/library/ee340463.aspx)。感谢您的澄清。 【参考方案1】:

是的,使用了在这个 SO 答案“Overload operator in 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 [Gist]:

//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

它使用运算符 |?-&gt; 代替。

【讨论】:

哇,这是可扩展的:static member Coalesce(a: 'a option ref, b: unit -&gt; 'a) = match a.Value with Some a -&gt; a | _ -&gt; b()。顺便说一句,您可以通过匹配 null 来放松等式约束 你也可以叫接线员|?如果你愿意。但我真的希望 F# 允许我们定义一个 ??操作员。 ;-) @luksan 您可以通过 F# User Voice 站点 visualstudio.uservoice.com/forums/121579-visual-studio/category/… 或直接向 microsoft dot com 上的 fsbugs 提出功能请求 我是 F# 新手。它适用于 Nullable 吗?尝试应用它时出现编译错误:“Nullable, int”类型均不支持运算符“|??”让 foo = nullableOfInt |?? 6 只是大声思考,而不是懒惰,是否可以放置一个计算表达式,即:let foo = nullableOfInt??? &lt;@ 10 @&gt;【参考方案2】:

由jbtule修改the accepted answer以支持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)

【讨论】:

我希望这个 coalesce 运算符支持 DBNull.Value 和 box (DBNull.Value),这段代码似乎可以做到这一点,我认为其他 F# 用户必须遇到相同的要求并且可能有 cmets 或修改。【参考方案3】:

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

【讨论】:

缺点是它只适用于选项并且提供默认值的表达式总是被评估。

以上是关于F#中的空合并运算符?的主要内容,如果未能解决你的问题,请参考以下文章

ES11中的空值合并运算符

PHP 中 C# 的空值合并运算符 (??)

带返回的空合并运算符 (??)

为啥 PHP 的空合并运算符 (??) 不能处理具有不同可见性的类常量?

如何修复颤振中的“未处理的异常:用于空值的空检查运算符”错误?

用于空值的空检查运算符