如何确定union case属性是否由编译器命名

时间:2016-09-14 18:38:20

标签: .net f#

F#允许您选择命名联合案例属性。 如果不这样,编译器将在编译期间自动为您选择一个名称(Item1 ... n)。

type Foo =
| Nothing
| AutomaticallyNamed of string
| Named of nameOfProperty: string

我唯一能想到的就是GetFields返回的名称上的简单字符串比较。

let testCase = Named "bar"
let caseInfo, _ = FSharpValue.GetUnionFields(testCase, typeof<Foo>)

caseInfo.GetFields()

是否有更可靠的方法来确定编译器是否生成了名称?

1 个答案:

答案 0 :(得分:3)

非常有趣的问题。事实证明,case字段实际上并不存储为System.Tuple,而是编译成标准.NET属性,在嵌套类上有一个代表工会案例的支持字段。

如果您查看ILSpy或类似反汇编程序中生成的代码,您会发现一些奇怪的部分:

  1. 该属性没有任何内容可以告诉您它有一个用户提供的名称,所以如果您在这里寻找一个漂亮而干净的解决方案,那你就不走运了。

  2. 但是,当字段具有用户提供的名称时,将使用名称前面的下划线(即_nameOfProperty)生成支持字段(不是属性)。因此,如果在union case嵌套类上调用GetMembers,则可以使用反射向下钻取,提供NonPublic ||| Instance绑定标记。

  3. 或者,相同的名称用于union case类的单个非公共构造函数的构造函数参数,因此您可以通过再次检查构造函数的参数来获取相同的信息 - GetConstructors使用非公共绑定标志,然后GetParameters

  4. 事情从来都不是这么简单 - 这里有一点需要注意:如果你使用编译器使用的命名方案命名你的字段 - 即Item42 - 它将被编译而没有下划线,就像它由编译器分配。虽然幸运的是编译器很聪明,可以跳过你已经使用的序数。

  5. 因此,理论上您可以检测该名称是否是用户提供的,但它很麻烦,并且依赖于内部实施细节,这些细节不能保证保持不变。