首页 > 解决方案 > 具有多个基本构造函数和事件的 F# 继承

问题描述

目前有一个像这样实现的 F# 类:

namespace MultiLanguage.FSharpClassLibrary

open MultiLanguage.CSharpClassLibrary
open System
open System.Runtime.Serialization

[<Serializable>]
type SerializableFSharpClass =
    inherit SerializableBaseClass

    new () as this =
        { inherit SerializableBaseClass() }
        then
        this.InitFields()

    new (other: SerializableFSharpClass) as this =
        { inherit SerializableBaseClass(other) }
        then
        this.InitFields()
        // Copy Properties

    new (info : SerializationInfo, context : StreamingContext) as this
        = { inherit SerializableBaseClass(info, context) } then
        this.InitFields()
        // Deserialize Properties

    // Let binding does not work
    // because of the following error:
    // FS0963: This definition may only be used in a type with a primary constructor.
    //let myFSharpEvent = new Event<EventHandler<EventArgs>,EventArgs>()
    //[<CLIEvent>]
    //member this.FSharpEvent = myFSharpEvent.Publish

    // Event raising does not work with member private
    // because new instances are created with every access to FSharpEvent:
    //member private this.myFSharpEvent = new Event<EventArgs>()
    //[<CLIEvent>]
    //member this.FSharpEvent = this.myFSharpEvent.Publish
    //member this.RaiseFSharpEvent e = this.myFSharpEvent.Trigger e

    // Event raising works with val mutable
    // but requires additional initialization of myFSharpEvent
    [<DefaultValue>]
    val mutable myFSharpEvent : Event<EventHandler<EventArgs>,EventArgs>
    [<CLIEvent>]
    member this.FSharpEvent = this.myFSharpEvent.Publish
    member this.RaiseFSharpEvent e = this.myFSharpEvent.Trigger e

    member private this.InitFields()
        =
        this.myFSharpEvent <- new Event<EventHandler<EventArgs>,EventArgs>()

我想知道是否有一种更简单的方法让 CLIEvent 与 let 绑定一起工作,并且仍然根据需要使用所有基类构造函数来最终摆脱 InitFields 方法。

标签: c#.netf#

解决方案


恐怕你在这里无能为力。问题是,只有当您总是只调用一个基类构造函数时,才能使用具有隐式构造函数的类的轻量级 F# 语法。您的情况并非如此,因此您必须使用显式语法。

DefaultValue如果在构造函数部分初始化事件,则可以避免该属性{ .. },您可以在其中调用基类构造函数并初始化所有字段。这对您并没有太大帮助,但这是我能想到的唯一调整:

type A = 
  new (n:int) = {}
  new (s:string) = {}

type B =
  inherit A

  new (n:int) = { 
    inherit A(n) 
    myFSharpEvent = new Event<_, _>() }
  new (s:string) = { 
    inherit A(s) 
    myFSharpEvent = new Event<_, _>() }

  val mutable myFSharpEvent : Event<EventHandler<EventArgs>,EventArgs>
  member this.FSharpEvent = this.myFSharpEvent.Publish
  member this.RaiseFSharpEvent e = this.myFSharpEvent.Trigger e

这会导致一些最小的代码重复——我认为这很好,但你也可以定义一个辅助函数(但在类之外,因为你不能let在这个类中使用)。

总结可能是我将把所有重要的逻辑从这个类中移走,只把它当作一个包装器来使我漂亮的 F# 代码与你需要的任何 .NET 基础结构兼容。


推荐阅读