简体   繁体   English

F# Inheritance 具有多个基础构造函数和事件

[英]F# Inheritance with Multiple Base Constructors and Events

Currently there is an F# class implemented like this:目前有一个 F# class 实现如下:

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.我想知道是否有更简单的方法让 CLIEvent 与 let 绑定一起使用,并且仍然根据需要使用所有基本 class 构造函数,以最终摆脱 InitFields 方法。

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.问题在于,只有当您始终只调用单个基本 class 构造函数时,才能使用具有隐式构造函数的类的轻量级 F# 语法。 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.如果在构造函数的{.. }部分初始化事件,则可以避免DefaultValue属性,您可以在其中调用基本 class 构造函数并初始化所有字段。 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).这会导致一些最小的代码重复 - 我认为这很好,但您也可以定义一个帮助程序 function (但在 class 之外,因为您不能在此类中使用let )。

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.总结可能是,我将把所有重要的逻辑从这个 class 中移开,只把它当作一个包装器,以使我漂亮的 F# 代码与你需要的任何 .NET 基础设施兼容。

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

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