简体   繁体   中英

F# Inheritance with Multiple Base Constructors and Events

Currently there is an F# class implemented like this:

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>()

I am wondering if there is an easier way to have both CLIEvent working with let binding and still use all the base class constructors as required to finally get rid of the InitFields method.

I'm afraid there is not much you can do here. The issue is that, the lightweight F# syntax for classes with implicit constructor can only be used if you always call just a single base class constructor. This is not the case in your situation and so you have to use the explicit syntax.

You can avoid the DefaultValue attribute if you initialize the event in the {.. } part of the constructor, where you can call the base class constructor and also initialize all fields. This does not really help you much, but it's the only tweak I can think of:

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

This leads to some minimal code duplication - I think that's fine, but you could also define a helper function (but outside of the class, because you cannot use let in this class).

The summary probably is that I would move all important logic away from this class and only treat this as a wrapper to make my nice F# code compatible with whatever .NET infrastructure you need.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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