F#可变,没有MEF的初始值

Jiz*_*ugu 1 f# mef

我有一堆导出IModule接口的模块.所以在主程序中我没有问题

...
let mutable modules = Seq.empty
[<ImportMany>]
member x.Modules
    with get():IEnumerable<Lazy<IModule, IModuleData>> = modules
    and set(a) = modules <- a
...
Run Code Online (Sandbox Code Playgroud)

但是现在我需要将一个接口公开给那些模块.因此每个模块都将导入一个接口

...
let mutable parent:IParent = ?
[<Import>]
member x.Parent
    with get():IParent = parent
    and set(a) = parent <- a
...
Run Code Online (Sandbox Code Playgroud)

所以我的问题是,当我没有初始值时,如何创建我的可变"父"?此外,这是将API公开回组件部分的适当方法吗?

Tom*_*cek 6

使用Unchecked.defaultof<_>应该做的伎俩,但这意味着你正在规避F#类型系统,这可能是一件危险的事情 - 系统会试图阻止你意外取消引用null值(和获取NullReferenceException).

在F#中声明的类型没有null正确的值,这是为了消除由此引起的常见错误null.干净的F#方法是使用选项类型来表示缺少值的事实:

let mutable parent:option<IParent> = None

[<Import>] 
member x.Parent 
   with get():IParent = 
      match parent with
      | Some p -> p
      | None -> failwith "TODO: Throw some reasonable exception here!"
   and set(a) = parent <- Some(a)
Run Code Online (Sandbox Code Playgroud)

如果你只想说IParent可以有一个null值(也许是因为你需要在一些C#代码中使用它而忽略了F#限制),那么你可以使用允许null与类型一起使用的特殊属性来标记类型定义.

[<AllowNullLiteral>]
type IParent = 
  abstract DoStuff : unit -> unit
Run Code Online (Sandbox Code Playgroud)

然后你就可以写了let mutable parent:IParent = null.这种方法的好处是,您还可以轻松检查一个值是否null(仅使用if parent <> null then ...)在您使用时不是那么明显Unchecked.defaultof<_>.