33

In C# I can do:

var castValue = inputValue as Type1

In F#, I can do:

let staticValue = inputValue :> Type1
let dynamicValue = inputValue :?> Type1

But neither of them is the equivalent of the C#'s keyword as.

I guess I need to do a match expression for the equivalent in F#

match inputValue with
| :? Type1 as type1Value -> type1Value
| _ -> null

Is this correct?

knocte
  • 16,941
  • 11
  • 79
  • 125
Nick Randell
  • 17,805
  • 18
  • 59
  • 74

5 Answers5

37

As far as I know, F# doesn't have any built-in operator equivalent to C# as so you need to write some more complicated expression. Alternatively to your code using match, you could also use if, because the operator :? can be use in the same way as is in C#:

let res = if (inputValue :? Type1) then inputValue :?> Type1 else null

You can of course write a function to encapsulate this behavior (by writing a simple generic function that takes an Object and casts it to the specified generic type parameter):

let castAs<'T when 'T : null> (o:obj) = 
  match o with
  | :? 'T as res -> res
  | _ -> null

This implementation returns null, so it requires that the type parameter has null as a proper value (alternatively, you could use Unchecked.defaultof<'T>, which is equivalent to default(T) in C#). Now you can write just:

let res = castAs<Type1>(inputValue)
Tomas Petricek
  • 240,744
  • 19
  • 378
  • 553
  • This works if the type is static. Any idea what to do if the type is defined at runtime? I'm looking for a F# equivalent of http://stackoverflow.com/a/19068042/23059. – Cameron Taggart Dec 28 '15 at 03:12
14

I would use an active pattern. Here is the one I use:

let (|As|_|) (p:'T) : 'U option =
    let p = p :> obj
    if p :? 'U then Some (p :?> 'U) else None

Here is a sample usage of As:

let handleType x = 
    match x with
    | As (x:int) -> printfn "is integer: %d" x
    | As (s:string) -> printfn "is string: %s" s
    | _ -> printfn "Is neither integer nor string"

// test 'handleType'
handleType 1
handleType "tahir"
handleType 2.
let stringAsObj = "tahir" :> obj
handleType stringAsObj
Tahir Hassan
  • 5,715
  • 6
  • 45
  • 65
  • 3
    This is unnecessary... as the original question notes, F# pattern matching has this built in. You can `| :? int as i -> i`. – Dan Fitch Aug 09 '16 at 22:06
  • is this functionally any different than using `| :?` as @DanFitch suggests? – Maslow Sep 21 '16 at 14:15
  • 4
    found a majorly useful difference. you can do Post-cast sub-matching without a when clause. `function |As(Some true) -> () | _ -> ()` for instance – Maslow Feb 22 '18 at 19:06
  • 1
    Note that post-cast matching is now possible in F# 6 (https://github.com/fsharp/fslang-design/blob/main/FSharp-6.0/FS-1105-Non-variable-patterns-to-the-right-of-as-patterns.md), so you should really use the built-in syntax as @DanFitch mentions – chkn Mar 06 '22 at 19:30
6

You can create your own operator to do this. This is virtually identical to Tomas's example, but shows a slightly different way to call it. Here's an example:

let (~~) (x:obj) = 
  match x with
  | :? 't as t -> t //'
  | _ -> null

let o1 = "test"
let o2 = 2
let s1 = (~~o1 : string)  // s1 = "test"
let s2 = (~~o2 : string) // s2 = null
kvb
  • 54,864
  • 2
  • 91
  • 133
4

Yes; see, except below from: What does this C# code look like in F#? (part one: expressions and statements)

C# has "is" and "as" operators for type tests. F# uses a particular pattern in a match for this. So this C# code:

    if (animal is Dog)
    {
        Dog dog = animal as Dog;
        // …
    }
    else if (animal is Cat)
    {
        Cat cat = animal as Cat;
        // …
    }

becomes this F# code:

    match animal with
    | :? Dog as dog -> // …
    | :? Cat as cat -> // …

where ":? type" is a type test, and "as ident" names the resulting value of that type if the type test succeeds.

Józef Podlecki
  • 10,453
  • 5
  • 24
  • 50
Brian
  • 117,631
  • 17
  • 236
  • 300
  • http://meta.stackexchange.com/questions/225370/your-answer-is-in-another-castle-when-is-an-answer-not-an-answer link only answer, not to another stackoverflow detailed answer. – Maslow Sep 21 '16 at 14:21
1

I guess I need to do a match expression for the equivalent in F#

match inputValue with | :? Type1 as type1Value -> type1Value | _ -> null

Is this correct?

Yes, it's correct. (Your own answer is better than the rest of the answers in my opinion.)

knocte
  • 16,941
  • 11
  • 79
  • 125