【问题标题】:F# inheritance with multiple constructors syntax具有多个构造函数语法的 F# 继承
【发布时间】:2018-06-29 15:32:55
【问题描述】:

我有这个 F# 类

module File1

open System
open System.Collections.Generic

type TimeRangeList<'e>(getter: DateTime * DateTime -> List<'e>, ?maybe_tFrom: DateTime, ?maybe_tTo: DateTime) as this = 
    inherit List<'e>()
    //inherit List<'e>(getter(defaultArg maybe_tTo DateTime.Now, defaultArg maybe_tFrom ((defaultArg maybe_tTo DateTime.Now).AddDays(-1.0))))

    let tTo = defaultArg maybe_tTo DateTime.Now
    let tFrom = defaultArg maybe_tFrom (tTo.AddDays(-1.0))
    do this.AddRange(getter(tFrom, tTo))

现在我想添加构造函数并使用here中的语法

type TimeRangeList<'e> = 
    inherit List<'e>
    val tFrom: DateTime
    val tTo: DateTime
    new (getter: DateTime * DateTime -> List<'e>, ?maybe_tFrom: DateTime, ?maybe_tTo: DateTime) = {
            inherit List<'e>()
            //inherit List<'e>(defaultArg maybe_tFrom ((defaultArg maybe_tTo DateTime.Now).AddDays(-1.0)), getter(defaultArg maybe_tTo DateTime.Now))

            tTo = defaultArg maybe_tTo DateTime.Now
            tFrom = defaultArg maybe_tFrom (tTo.AddDays(-1.0)) //tTo undefined
            //tFrom = defaultArg maybe_tFrom ((defaultArg maybe_tTo DateTime.Now).AddDays(-1.0))
        }
    do this.AddRange(getter(tFrom, tTo)) //primary constructor required

这段代码给出了两个错误:

  1. 在 'tFrom=...' 中显示 'tTo not defined' 而 tTo 显然在 范围;作为一种解决方法,我可以重复 defaultArg 调用,如图所示 以下(注释)行。有没有更好的方法?
  2. 在调用“AddRange”的最后一行,它抱怨说do 调用只能在主构造函数中执行,这是公平的。但是,如何调用必要的 AddRange 来初始化列表?我尝试了不同的选择,但找不到方法。在注释的继承行中显示了一种解决方法,但最后我重复且冗余地调用 defaultArg;一定有更清晰更优雅的方式

【问题讨论】:

  • 实际上,您的代码示例中最大的问题是您的类型定义不是类定义,因为您省略了括号。你应该写type TimeRangeList&lt;'e&gt;() =。这不会修复您的代码,但您会遇到更有意义的错误。
  • @ThomasCorbière 我知道这一点,但这是官方文档对多个构造函数所做的(按照两个代码摘录之间的链接“这里”)。使用通常的默认构造函数语法会强制所有其他构造函数调用主构造函数,而主构造函数又调用一个特定的基本构造函数。如果我们想要不同的派生构造函数调用不同的基构造函数,则使用的语法是没有主构造函数的语法。
  • 哦,对不起,我错过了!我找到了第一个问题的解决方案,请参阅下面的答案。
  • "...您的类型定义不是类定义,因为您省略了括号。"那是不对的。静态类没有构造函数/括号。

标签: inheritance constructor f#


【解决方案1】:

这是您要查找的语法:

module File1

open System
open System.Collections.Generic

type TimeRangeList<'e> = 
    inherit List<'e>
    val tFrom: DateTime
    val tTo: DateTime
    new (getter: DateTime * DateTime -> List<'e>, ?maybe_tFrom: DateTime, ?maybe_tTo: DateTime) as this =
        let to_ = defaultArg maybe_tTo DateTime.Now
        let from_ = defaultArg maybe_tFrom (to_.AddDays(-1.0))
        {
            inherit List<'e>()

            tTo = to_
            tFrom = from_
        }
        then
            this.AddRange(getter(this.tFrom, this.tTo))

文档链接:

稍微解释一下,{ field = value; field2 = value2 } 语法不必是在定义辅助构造函数的new() 块中找到的only 表达式。它只需要是 last 表达式,即返回的表达式。 (这里,即使从技术上讲,then 块是构造函数中的“最后一个”块,它的返回值(必须是 unit)被忽略,构造函数的实际返回值是最后一个表达式 then 块中找不到)。因此,之前使用let 表达式来定义要放入类字段中的值是安全的,并且这些let 表达式可以像在普通代码中一样相互引用。因此,如果您需要将复杂或昂贵的计算放入多个字段中,您可以这样:

new () =
    let result = expensiveCalculationIWantToDoOnlyOnce()
    { field1 = result; field2 = result + 1; field3 = result + 2 }

【讨论】:

  • 感谢您提供宝贵的信息。不过,官方文档中的更多细节不会有什么坏处,恕我直言
  • @FrancoTiveron - 同意,文档可能会更好。但是,如果您向下滚动到文档底部,您可以通过 Github 提交 PR 以改进文档。是的,微软已经将他们的文档开源了!所以现在,当你发现一些记录不充分的东西时,你至少有能力对它做点什么。您可能没有时间(我现在没有),但我很高兴改进文档的选项确实可用。
【解决方案2】:

要解决第一个问题,您应该为当前对象命名:new (...) as this =,然后使用它访问您的变量this.tTo.AddDays(-1.0)

我还没有第二个问题的解决方案。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-01-17
    • 1970-01-01
    • 2011-07-06
    • 1970-01-01
    • 1970-01-01
    • 2017-03-11
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多