f# - 如何在 F# 中一般处理选项值

标签 f# option idatareader

我正在编写一个适配器类来将 IEnumerable<'T> 映射到 IDataReader,完整的源代码位于 https://gist.github.com/jsnape/56f1fb4876974de94238供引用,但我想问一下编写其中一部分的最佳方式。即两个函数:

member this.GetValue(ordinal) =
    let value = match enumerator with
        | Some e -> getters.[ordinal](e.Current)
        | None -> raise (new ObjectDisposedException("EnumerableReader"))

    match value with
        | :? Option<string> as x -> if x.IsNone then DBNull.Value :> obj else x.Value :> obj
        | :? Option<int> as x -> if x.IsNone then DBNull.Value :> obj else x.Value :> obj
        | :? Option<decimal> as x -> if x.IsNone then DBNull.Value :> obj else x.Value :> obj
        | :? Option<obj> as x -> if x.IsNone then DBNull.Value :> obj else x.Value
        | _ -> value

此函数必须返回一个对象,但由于传递的值可以是下游函数(例如 SqlBulkCopy)无法理解的任何 F# 选项类型,因此我需要解压缩该选项并将其转换为 null/DBNull。

上面的代码有效,但我觉得它有点笨拙,因为我必须为不同的类型( float 等)添加新的特化。我确实尝试使用通配符 | :? Option <_> as x -> 在匹配中,但编译器给了我一个'不那么通用的警告,代码只会匹配 Option< obj >。

这怎么写得更地道呢?我怀疑主动模式可能会起作用,但我从未使用过它们。

对于这个其他函数,类似:
member this.IsDBNull(ordinal) =
    match (this :> IDataReader).GetValue(ordinal) with
        | null -> true
        | :? DBNull -> true
        | :? Option<string> as x -> x.IsNone
        | :? Option<int> as x -> x.IsNone
        | :? Option<decimal> as x -> x.IsNone
        | :? Option<obj> as x -> x.IsNone
        | _ -> false

我不在乎它是什么类型的 Option 我只想检查 IsNone

最佳答案

我认为你应该使用一些像这样的反射技术:

open System

let f (x:obj) =
    let tOption = typeof<option<obj>>.GetGenericTypeDefinition()
    match x with
    | null -> printfn "null"; true
    | :? DBNull -> printfn "dbnull"; true
    | _ when x.GetType().IsGenericType && x.GetType().GetGenericTypeDefinition() = tOption ->
        match x.GetType().GenericTypeArguments with
        | [|t|] when t = typeof<int> -> printfn "option int"; true
        | [|t|] when t = typeof<obj> -> printfn "option obj"; true
        | _                          -> printfn "option 't" ; true

    | _ -> printfn "default"; false


let x = 4 :> obj
let x' = f x  //default

let y = Some 4 :> obj
let y' = f y  // option int

let z = Some 0.3 :> obj
let z' = f z  // option 't

更新

事实上,如果您只是有兴趣检查所有选项类型的 IsNone 情况并且不想使用反射,那么您不需要其他情况,它们将属于空情况,因为 None 被编译为空。例如使用上一个函数试试这个:
let y1 = (None: int option)  :> obj
let y1' = f y1  // null

let z1 = (None: float option)  :> obj
let z1' = f z1  // null

它正在处理第一种情况(空情况)

对于 GetValue 成员,我查看了您的要点,并且由于您已经在包含该成员的类型中定义了泛型 'T,您可以只写:
match value with
| :? Option<'T> as x -> if x.IsNone then DBNull.Value :> obj else x.Value :> obj

对于所有选项类型。

关于f# - 如何在 F# 中一般处理选项值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24841185/

相关文章:

f# - 函数自动向上转换的规则是什么?

f# - Deedle 标准化帧

f# - 在 F# 中实现幻像类型

c++ - 'gcc -g'和'gcc -g3'有什么区别?

forms - Symfony2更改嵌入式表单的字段选项

c# - 在Visual Studio 2012中有一种在调试C#时使用F#REPL的方法

javascript - 如何修改此 jQuery 以在下拉列表中动态显示和隐藏相关选择选项?

c# - 如何使用 automapper 映射具有多个表的数据集

C# 读者处置审查

c# - 检查数据库记录映射中的空值