简体   繁体   English

如何在F#中创建一个值类型的联合类型?

[英]How to create a Union type in F# that is a value type?

Normal F# Discriminated Unions are reference types. 普通F#区分联合是引用类型。 How can I create a simple (non-recursive and with only value-type fields) union type in F# that is a value type? 如何在F#中创建一个简单的 (非递归的,只有值类型的字段)联合类型,它是一个值类型?

Based on some internet searching my current (non-working) attempt looks as follows: 根据一些互联网搜索,我当前(非工作)的尝试看起来如下:

[<StructLayout(LayoutKind.Explicit)>]
type Float =
    [<DefaultValue>] [<FieldOffset 0>] val mutable Val1 : float
    [<DefaultValue>] [<FieldOffset 0>] val mutable Int1 : int
    new (a:float) = {Val1 = a}    

The following blog post appears to show what is possible via C# 以下博客文章显示通过C#显示可能的内容

I'm aware that the above is NOT idiomatic use of F# but I am trying to optimize the performance of a portion of my application and profiling has clearly shown that the cost of heap allocations (JIT_new) is what is causing my performance bottleneck... A simple union type is the perfect data structure for my needs, just not a heap allocated one. 我知道上面的内容并不是惯用的F#,但我试图优化我的应用程序的一部分性能,并且分析清楚地表明堆分配(JIT_new)的成本是导致我的性能瓶颈的原因。简单的联合类型是满足我需求的完美数据结构,而不是堆分配的。

First of all, I would probably not do this, unless I had very good reasons. 首先,我可能无法做到这一点,除非我有非常好的理由。 In most cases, the difference between structs and reference types is not really that big - in my experience, it only matters when you have a very large array of them (then structs let you allocate one big memory chunk). 在大多数情况下,结构和引用类型之间的区别并不是那么大 - 根据我的经验,只有当你有一个非常大的数组(然后结构允许你分配一个大的内存块)时才重要。

That said, it looks like F# does not like the constructor code in your example. 也就是说,看起来F#不喜欢你的例子中的构造函数代码。 I'm really not sure why (it seems to be doing some check that does not quite work for overlapping structs), but the following does the trick: 我真的不确定为什么(它似乎做了一些检查对于重叠结构不太有效),但以下是诀窍:

[<Struct; StructLayout(LayoutKind.Explicit)>]
type MyStruct =
    [<DefaultValue; FieldOffset 0>] 
    val mutable Val1 : float
    [<DefaultValue; FieldOffset 0>] 
    val mutable Int1 : int
    static member Int(a:int) = MyStruct(Int1=a)
    static member Float(f:float) = MyStruct(Val1=f)

If I actually wanted to use this, I would add another field Tag containing 1 or 0 depending on which case your struct represents. 如果我真的想要使用它,我会添加另一个包含10字段Tag ,具体取决于结构所代表的情况。 Then you could pattern match on it using an active pattern and get some of the safety of discriminated unions back: 然后,您可以使用活动模式对其进行模式匹配,并获得一些有区别的工会的安全性:

let (|Float|Int|) (s:MyStruct) = 
  if s.Tag = 0 then Float(s.Val1) else Int(s.Int1)

Struct unions are now supported by F#, see F# RFC FS-1014 for details. 现在,F#支持结构联合,有关详细信息,请参阅F#RFC FS-1014 In short: 简而言之:

 // Single case: [<Struct>] type UnionExample = U of int * int * bool // Multi-case: [<Struct>] type Shape = | Circle of radius: double | Square of side: int 

Key differences in struct records: 结构记录的主要区别:

  • You cannot have cyclic references to the same type being defined. 您不能对定义的相同类型进行循环引用。 ex: type T = U of T 例如:类型T = U的T
  • You also cannot call the default ctor, like you could with normal F# structs. 您也无法调用默认的ctor,就像使用普通的F#结构一样。
  • For multi-case struct unions, each case must have a unique name. 对于多案例结构联合,每个案例必须具有唯一的名称。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM