首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何从obj降到option<obj>?

如何从obj降到option<obj>?
EN

Stack Overflow用户
提问于 2011-06-09 07:58:16
回答 3查看 2.8K关注 0票数 12

我有一个函数,它接受一个类型为object的参数,并需要将其向下转换为一个option<obj>

代码语言:javascript
复制
member s.Bind(x : obj, rest) =
    let x = x :?> Option<obj>

如果我将一个'Microsoft.FSharp.Core.FSharpOption'1System.String‘类型的对象作为x传递(例如),那么最后一行将抛出异常:无法将类型为Option<string>的对象转换为'Microsoft.FSharp.Core.FSharpOption'1System.Object'.类型。

或者,如果我尝试类型测试:

代码语言:javascript
复制
member s.Bind(x : obj, rest) =
   match x with
    | :? option<obj> as x1 -> ... // Do stuff with x1
    | _ -> failwith "Invalid type"

那么x就永远无法与option<obj>相匹配。

为了使此工作正常进行,我目前必须指定选项包含的类型(例如,如果函数被传递为option<string>,并且我将参数向下转换为该类型,而不是option<obj>,则该函数可以工作。

是否有一种方法可以将参数降为option<obj>而不指定该选项包含的类型?我尝试过option<_>option<#obj>option<'a>,结果都是一样的。

作为背景,参数需要类型为obj,因为我正在为monad编写接口,因此Bind需要绑定不同类型的值,具体取决于实现接口的monad。这个特定的monad是一个延续monad,所以它只想确保参数是Some(x)而不是None,然后将x传递给rest。(我需要这个接口的原因是我正在编写一个monad转换器,我需要一种方法来告诉它它的参数monad实现绑定和返回。)

更新:在选项成为该函数的参数之前,通过向上转换该选项的内容来解决这个问题,但我仍然很想知道是否可以将一个对象(或泛型参数)键入或强制转换到一个选项中,而不必担心该选项包含什么样的类型(当然,该转换是有效的,也就是说,该对象实际上是一个选项)。

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2011-06-09 09:43:59

目前还没有解决这个问题的好办法。

问题是您需要在模式匹配中引入一个新的泛型类型参数(当与option<'a>匹配时),但是F#只允许在函数声明中定义泛型类型参数。所以,您唯一的解决方案是使用一些反射技巧。例如,您可以定义一个活动模式,该模式隐藏以下内容:

代码语言:javascript
复制
let (|SomeObj|_|) =
  let ty = typedefof<option<_>>
  fun (a:obj) ->
    let aty = a.GetType()
    let v = aty.GetProperty("Value")
    if aty.IsGenericType && aty.GetGenericTypeDefinition() = ty then
      if a = null then None
      else Some(v.GetValue(a, [| |]))
    else None

这将为您提供包含任意选项类型的NoneSome,其中包含obj

代码语言:javascript
复制
let bind (x : obj) rest =   
    match x with    
    | SomeObj(x1) -> rest x1
    | _ -> failwith "Invalid type"

bind(Some 1) (fun n -> 10 * (n :?> int))
票数 9
EN

Stack Overflow用户

发布于 2011-06-09 08:22:57

我不知道为什么您需要将输入作为obj,但是如果您的输入是Option<_>,那么很容易:

代码语言:javascript
复制
member t.Bind (x : 'a option, rest : obj option -> 'b) =
    let x = // val x : obj option
        x
        |> Option.bind (box >> Some)
    rest x
票数 3
EN

Stack Overflow用户

发布于 2011-06-09 19:42:04

要回答最后一个问题:如果您需要一种通用的方法来检查选项,而事先不使用装箱值,则可以使用Tomas代码的一些细微变化:

代码语言:javascript
复制
let (|Option|_|) value = 
  if obj.ReferenceEquals(value, null) then None
  else
    let typ = value.GetType()
    if typ.IsGenericType && typ.GetGenericTypeDefinition() = typedefof<option<_>> then
      let opt : option<_> = (box >> unbox) value
      Some opt.Value
    else None
//val ( |Option|_| ) : 'a -> 'b option    

let getValue = function
  | Option x ->  x
  | _ -> failwith "Not an option"

let a1 : int = getValue (Some 42)
let a2 : string = getValue (Some "foo")
let a3 : string = getValue (Some 42) //InvalidCastException
let a4 : int = getValue 42 //Failure("Not an option")
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/6289761

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档