How to downcast from obj to option?

前端 未结 3 1905
粉色の甜心
粉色の甜心 2021-01-11 12:49

I have a function that takes a parameter of type object and needs to downcast it to an option.

member s.Bind(x : obj, rest) =
    let         


        
相关标签:
3条回答
  • 2021-01-11 13:35

    To answer your last question: you can use a slight variation of Tomas' code if you need a general-purpose way to check for options without boxing values beforehand:

    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")
    
    0 讨论(0)
  • 2021-01-11 13:47

    There isn't any nice way to solve this problem currently.

    The issue is that you'd need to introduce a new generic type parameter in the pattern matching (when matching against option<'a>), but F# only allows you to define generic type parameters in function declarations. So, your only solution is to use some Reflection tricks. For example, you can define an active pattern that hides this:

    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
    

    This will give you None or Some containing obj for any option type:

    let bind (x : obj) rest =   
        match x with    
        | SomeObj(x1) -> rest x1
        | _ -> failwith "Invalid type"
    
    bind(Some 1) (fun n -> 10 * (n :?> int))
    
    0 讨论(0)
  • 2021-01-11 13:50

    I am not certain why you need to get your input as obj, but if your input is an Option<_>, then it is easy:

    member t.Bind (x : 'a option, rest : obj option -> 'b) =
        let x = // val x : obj option
            x
            |> Option.bind (box >> Some)
        rest x
    
    0 讨论(0)
提交回复
热议问题