.net 如何确定编译器是否命名了union case属性

.net 如何确定编译器是否命名了union case属性,.net,f#,.net,F#,F#允许您选择命名联合案例属性。 如果没有,编译器将在编译期间自动为您选择一个名称(Item1…n) 我唯一能想到的就是对返回的名称进行简单的字符串比较 让testCase=命名为“bar” 让caseInfo,=FSharpValue.GetUnionFields(testCase,typeof) caseInfo.GetFields() 有没有更可靠的方法来确定名称是否由编译器生成?这是一个非常有趣的问题。事实证明,case字段实际上并不是存储为System.Tuple,而是编译成标准的.

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

我唯一能想到的就是对返回的名称进行简单的字符串比较

让testCase=命名为“bar”
让caseInfo,=FSharpValue.GetUnionFields(testCase,typeof)
caseInfo.GetFields()

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

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

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

  • 属性中没有任何内容可以告诉您它有一个用户提供的名称,因此,如果您正在这里寻找一个好的、干净的解决方案,那么您就倒霉了

  • 但是,当某个字段具有用户提供的名称时,将在名称(即
    \u nameOfProperty
    )前面生成带下划线的支持字段(而不是属性)。因此,如果在union case嵌套类上调用
    GetMembers
    ,并提供
    NonPublic | | | Instance
    绑定标志,则可以使用反射深入到它

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

  • 但事情从来没有这么简单-这里有一个警告,如果您实际使用编译器使用的命名方案命名字段-即
    Item42
    -它将在编译时不带下划线,就像它是由编译器指定的一样。幸运的是,编译器足够聪明,可以跳过您已经使用过的序号


  • 因此,从理论上讲,您可以检测名称是否由用户提供,但这是一个繁琐、环境性的问题,而且取决于内部实现细节,而这些细节并不能保证保持原样。

    我很惊讶这些名称在编译过程中没有被删除
    Item1..n
    只是标准的
    System.Tuple
    元素名称。这是我没有想到的一种可能性,但我很高兴这些名称仍然存在。但是是的,Item1..n是标准系统。元组名称。如果您只有一个未命名的属性,它将该属性命名为“Item”。我的.net IL-foo不够强大,但我用LinqPad做了一个快速的示例:我希望其他人会得出不同的结论。我也注意到了背景字段中的下划线。但这似乎没有提供比检查属性名称更确定的信息。所以我想我要用我已经有的东西。因为只有我会使用这些代码,所以我应该很好。另一方面,如果我读对了,如果目标是.net<4,它会编译成一个System.Tuple。@Brunner:该文档描述的是在.net 4.0之前,BCL中没有
    System.Tuple
    ,而F#使用了
    FSharp.Core
    中存在的该类型的本地副本。这与DUs的编译方式无关。welp,你是对的,应该重新阅读主题的标题。
    type Foo =
    | Nothing
    | AutomaticallyNamed of string
    | Named of nameOfProperty: string
    
    let testCase = Named "bar"
    let caseInfo, _ = FSharpValue.GetUnionFields(testCase, typeof<Foo>)
    
    caseInfo.GetFields()