Inheritance 对象表达式和错误FS0419:';基数';值只能用于直接调用重写成员的基本实现
标题中的错误是含糊不清的,但目前在某某上有两次点击,总共有五次(这表明它是一种罕见的野兽,所以不要期望在这里有太多的访问;)。我预计这个问题将排在第六位:p 表明错误文本具有误导性,如果从闭包中调用base,则会引发错误文本,但情况似乎并非总是如此,因为这样做有效:Inheritance 对象表达式和错误FS0419:';基数';值只能用于直接调用重写成员的基本实现,inheritance,compiler-errors,f#,overriding,fsunit,Inheritance,Compiler Errors,F#,Overriding,Fsunit,标题中的错误是含糊不清的,但目前在某某上有两次点击,总共有五次(这表明它是一种罕见的野兽,所以不要期望在这里有太多的访问;)。我预计这个问题将排在第六位:p 表明错误文本具有误导性,如果从闭包中调用base,则会引发错误文本,但情况似乎并非总是如此,因为这样做有效: // in a closure, but no FS0419 let myObj = fun foo -> { new obj() with override this.ToString() = base.ToStri
// in a closure, but no FS0419
let myObj = fun foo ->
{ new obj() with override this.ToString() = base.ToString() + foo}
但这是失败的(我发现的最简单的例子):
我使用对象表达式代替继承的类型声明,并尝试通过构建自定义NUnit约束来创建新的FsUnit约束。下面是一个简化版本,显示了我所看到的问题:
let EndsWithIgnoreWhitespaceContraint expectedResult =
{ new EndsWithConstraint(expectedResult) with
override __.ApplyTo<'T> (actual: 'T) =
let actual = box actual
if actual :? string then
actual :?> string
|> fun s -> if isNull s then String.Empty else s
|> fun s -> s.Trim()
// error FS0419: 'base' values may only be used to make direct
// calls to the base implementations of overridden members
|> base.ApplyTo
else
exn "String expected .. bla bla..." |> raise }
// make it available to FsUnit's syntax style (overriding existing endWith)
let endWith = EndsWithIgnoreWhitespaceContraint
// using it:
" hello world " |> should endWith "world"
有人知道到底是什么原因导致了这个错误吗?首先,您误解了“在闭包中不能使用base”的答案。它是指捕获基本身的闭包-是捕获阻止它工作,而不是闭包本身。在您的
{new obj}
示例中,base
不会被任何闭包捕获。捕获整个对象,但base
仅在ToString
方法中直接使用
要进行说明,请尝试以下方法:
let myObj =
{ new obj() with override this.ToString() = (fun() -> base.ToString())()}
let myObj =
{ new obj() with
override this.ToString() =
let f = base.ToString // Error here
f()
}
let myObj =
{ new obj() with
override this.ToString() =
() |> base.ToString // Same error
}
此代码不会编译,因为闭包fun()->base.ToString()
正在捕获base
其次,使用对象方法作为函数并不像人们所期望的那样“直接”工作,因为.NET方法的表示方式不同于F#函数。相反,当遇到类似于let x=obj.M
的问题时,编译器会将其视为let x=fun a->obj.M(a)
——也就是说,将其包装在闭包中
要进行说明,请尝试以下方法:
let myObj =
{ new obj() with override this.ToString() = (fun() -> base.ToString())()}
let myObj =
{ new obj() with
override this.ToString() =
let f = base.ToString // Error here
f()
}
let myObj =
{ new obj() with
override this.ToString() =
() |> base.ToString // Same error
}
看到了吗?:-)
当您将管道导入对象方法时,编译器必须创建该闭包,然后将其传递给管道操作符。要进行说明,请尝试以下方法:
let myObj =
{ new obj() with override this.ToString() = (fun() -> base.ToString())()}
let myObj =
{ new obj() with
override this.ToString() =
let f = base.ToString // Error here
f()
}
let myObj =
{ new obj() with
override this.ToString() =
() |> base.ToString // Same error
}
有趣的是,我在问题中添加了您的最后一个示例,就在您发布此消息之前;)。我不知道调用对象的方法时通常会创建隐式闭包。但是当我编译
x |>f
时,它被编译成fa
(iirc),所以按照你的故事,这个错误是由语义差异引起的(我没有注意到)。在x |>obj.f
中,编译器不知道(或未优化以知道)是否obj.f
将以闭包结束,如隐式f'=obj.f;f'a
,或显式(借助内联)obj.f a
。最终,一切都得到了优化,因此将管道引入方法不会带来性能开销。但是,错误检查发生在优化之前,因此不允许将管道引入到base
方法中。因此,作为旁白,我们在这里得出结论,a |>f
,即使是内联的,与f a
(优化之前)几乎相同,但并不完全相同。当然,这是一堂有趣的课!所有这些边缘案例最终都来自于必须生活在.NET基础设施中。这就是为什么尽可能避免上课的原因。是的,当然没有。原则上,base
值完全可以在闭包中捕获,只需捕获与正常值稍有不同的值,这就是它需要特别努力实现的原因。