Natalie Perret
Natalie Perret

Reputation: 8997

F#: How to get the type of an instance of an empty discriminated union case using Reflection?

I read the answers of those questions:

But I found out something surprising about the underlying type of discriminated unions:

type Union1 =
    | A
    | B 

type Union2 =
    | A
    | B of int

[<EntryPoint>]
let main argv =
    printfn "%A" (Union1.A.GetType())
    printfn "%A" (Union1.B.GetType())

    printfn "%A" (Union2.A.GetType())
    printfn "%A" (Union2.B(32).GetType())
    0
Program+Union1
Program+Union1
Program+Union2+_A
Program+Union2+B

Hence my question how I can discriminate a case based on the type when a case is empty?

Upvotes: 2

Views: 625

Answers (1)

Tomas Petricek
Tomas Petricek

Reputation: 243041

There is no way to distinguish between two union cases with no parameters based on type, because their type in compiled .NET code is the same. You can see that this is the case by slightly tweaking your example:

type Union1 = A | B     
Union1.A.GetType() = Union1.B.GetType() // Returns 'true'

The F# compiler compiles Union1 as a class with a numerical Tag field. For cases with no additional parameters like A and B here, it will just create an instance of Union1 and set the Tag field to 0 or 1.

When you have a union case with additional parameters, then the compiler generates a new inherited class that then stores values of these parameters (which is why you get a different type for Union2.B).

From the F# perspective, values of a discriminated union have the same type (even if the way DUs are compiled means that the type can sometimes be an inherited class), so there is no reason to expect that you would be able to distinguish cases based on a type - if you have a case where you need this, you should probably reconsider your approach.

Upvotes: 7

Related Questions