如何创建具有函数成员和默认 public 构造函数的 F# 结构?
How to create an F# struct with a function member and a default public constructor?
我需要创建一个 struct
类型来传递给不受我控制的外部 class。 class 要求我的 struct
类型有一个 public 默认构造函数。我需要它有一个函数类型作为成员。
以下生成 error FS0001: A generic construct requires that the type 'MyStruct' have a public default constructor
:
[<Struct>]
type MyStruct =
val callBack: unit -> unit
new(cb: unit -> unit) = { callBack = cb }
type ExternalClass<'T when 'T : (new : unit -> 'T) and 'T : struct> () =
member val something = new 'T()
let c = new ExternalClass<MyStruct>()
如果成员的类型从 unit -> unit
更改为 int
,这将正常工作。
我试过使用 DefaultValue
属性,根据 docs 应该可以在函数成员上正常工作,但这会产生两个错误:error FS0765: Extraneous fields have been given values
和 error FS0696: This is not a valid object construction expression. Explicit object constructors must either call an alternate constructor or initialize all fields of the object and specify a call to a super class constructor.
如何创建合适的类型来满足外部 class 的约束?
这里的问题是类型 unit -> unit
没有默认值。这是一个较短的重现:
[<Struct>]
type MyStruct =
val callBack: unit -> unit
let s = MyStruct()
最后一行出现错误:The default, zero-initializing constructor of a struct type may only be used if all the fields of the struct type admit default initialization
类型unit -> unit
不允许默认初始化。它必须有一个函数类型的值,并且没有“默认”函数值这样的东西。
.NET 中的结构是这样工作的:每个结构总是有一个默认的构造函数,程序员无法实现它。相反,运行时使用默认值初始化所有字段。这背后的想法是使分配数组或结构非常便宜:您只需将内存块清零,瞧!
所以按照这个逻辑,你的 callBack
字段必须是零可输出的,但它不能,因为在 F# 中函数类型的变量不能是 null
.
它与 int
一起工作正常,因为 int
确实有一个默认值零。
现在,一个“好的”解决方案将取决于您实际尝试做什么。但在没有这些信息的情况下,我可以建议一些本地解决方法。
第一个选项 - 使字段具有类型 obj
(因此其默认值为 null
)并提供一个安全的访问器return 一个 option
:
[<Struct>]
type MyStruct =
val private callBack: obj
member this.Callback with get() = this.callBack |> Option.ofObj |> Option.map (fun o -> o :?> (unit -> unit))
new(cb: unit -> unit) = { callBack = cb }
Callback
访问器 属性 将 return 一个 (unit -> unit) option
,如果函数已初始化,则将是 Some
或 None
如果不是:
> MyStruct().Callback;;
val it : (unit -> unit) option = None
> MyStruct(fun() -> ()).Callback;;
val it : (unit -> unit) option = Some <fun:it@10>
第二个选项 - 将回调包装在可为 null 的类型中:
[<AllowNullLiteral>]
type ACallback(cb : unit -> unit) =
member val Callback = cb with get
[<Struct>]
type MyStruct =
val callBack: ACallback
new(cb: unit -> unit) = { callBack = ACallback cb }
然后:
> MyStruct().callBack;;
val it : ACallback = <null>
> MyStruct(fun() -> ()).callBack;;
val it : ACallback = FSI_0006+ACallback {Callback = <fun:it@30-1>;}
这(可以说)以额外分配为代价提供了更多的类型安全性。
此外,有可能获得 null
,但如果这是一个问题,您也可以将其包装在 option
类型的访问器中:
[<Struct>]
type MyStruct =
val private callBack: ACallback
member this.Callback with get() = this.callBack |> Option.ofObj |> Option.map (fun c -> c.Callback)
new(cb: unit -> unit) = { callBack = ACallback cb }
> MyStruct().Callback;;
val it : (unit -> unit) option = None
> MyStruct(fun() -> ()).Callback;;
val it : (unit -> unit) option = Some <fun:it@38-2>
我需要创建一个 struct
类型来传递给不受我控制的外部 class。 class 要求我的 struct
类型有一个 public 默认构造函数。我需要它有一个函数类型作为成员。
以下生成 error FS0001: A generic construct requires that the type 'MyStruct' have a public default constructor
:
[<Struct>]
type MyStruct =
val callBack: unit -> unit
new(cb: unit -> unit) = { callBack = cb }
type ExternalClass<'T when 'T : (new : unit -> 'T) and 'T : struct> () =
member val something = new 'T()
let c = new ExternalClass<MyStruct>()
如果成员的类型从 unit -> unit
更改为 int
,这将正常工作。
我试过使用 DefaultValue
属性,根据 docs 应该可以在函数成员上正常工作,但这会产生两个错误:error FS0765: Extraneous fields have been given values
和 error FS0696: This is not a valid object construction expression. Explicit object constructors must either call an alternate constructor or initialize all fields of the object and specify a call to a super class constructor.
如何创建合适的类型来满足外部 class 的约束?
这里的问题是类型 unit -> unit
没有默认值。这是一个较短的重现:
[<Struct>]
type MyStruct =
val callBack: unit -> unit
let s = MyStruct()
最后一行出现错误:The default, zero-initializing constructor of a struct type may only be used if all the fields of the struct type admit default initialization
类型unit -> unit
不允许默认初始化。它必须有一个函数类型的值,并且没有“默认”函数值这样的东西。
.NET 中的结构是这样工作的:每个结构总是有一个默认的构造函数,程序员无法实现它。相反,运行时使用默认值初始化所有字段。这背后的想法是使分配数组或结构非常便宜:您只需将内存块清零,瞧!
所以按照这个逻辑,你的 callBack
字段必须是零可输出的,但它不能,因为在 F# 中函数类型的变量不能是 null
.
它与 int
一起工作正常,因为 int
确实有一个默认值零。
现在,一个“好的”解决方案将取决于您实际尝试做什么。但在没有这些信息的情况下,我可以建议一些本地解决方法。
第一个选项 - 使字段具有类型 obj
(因此其默认值为 null
)并提供一个安全的访问器return 一个 option
:
[<Struct>]
type MyStruct =
val private callBack: obj
member this.Callback with get() = this.callBack |> Option.ofObj |> Option.map (fun o -> o :?> (unit -> unit))
new(cb: unit -> unit) = { callBack = cb }
Callback
访问器 属性 将 return 一个 (unit -> unit) option
,如果函数已初始化,则将是 Some
或 None
如果不是:
> MyStruct().Callback;;
val it : (unit -> unit) option = None
> MyStruct(fun() -> ()).Callback;;
val it : (unit -> unit) option = Some <fun:it@10>
第二个选项 - 将回调包装在可为 null 的类型中:
[<AllowNullLiteral>]
type ACallback(cb : unit -> unit) =
member val Callback = cb with get
[<Struct>]
type MyStruct =
val callBack: ACallback
new(cb: unit -> unit) = { callBack = ACallback cb }
然后:
> MyStruct().callBack;;
val it : ACallback = <null>
> MyStruct(fun() -> ()).callBack;;
val it : ACallback = FSI_0006+ACallback {Callback = <fun:it@30-1>;}
这(可以说)以额外分配为代价提供了更多的类型安全性。
此外,有可能获得 null
,但如果这是一个问题,您也可以将其包装在 option
类型的访问器中:
[<Struct>]
type MyStruct =
val private callBack: ACallback
member this.Callback with get() = this.callBack |> Option.ofObj |> Option.map (fun c -> c.Callback)
new(cb: unit -> unit) = { callBack = ACallback cb }
> MyStruct().Callback;;
val it : (unit -> unit) option = None
> MyStruct(fun() -> ()).Callback;;
val it : (unit -> unit) option = Some <fun:it@38-2>