所以我用F#在winforms中做了一个简单的个人项目。我的代码曾经工作过,但现在抛出这个异常似乎没有理由。
An unhandled exception of type 'System.InvalidOperationException' occurred in FSharp.Core.dll
Additional information: The initialization of an object or value resulted in an object or value being accessed recursively before it was fully initialized.
代码是从表单本身的构造函数调用的成员方法
do
//lots of other constructor code before this point
// render the form
form.ResumeLayout(false)
form.PerformLayout()
form.ReloadGoals
//several other members before here
member form.ReloadGoals =
let x = 10 //crashes on this line
我抓住我正在使用的项目的模板的网站是this one。 不幸的是,我已经为此做了一些实质性的补充。
我很乐意发布更多代码,但我需要知道哪些代码完全相关,因为我不确定并且不想在无关代码中陷入困境。
另外,我无法在System.InvalidOperationException上找到很多文档。 每次我找到它时,它都被用作异常an example的you can throw on your own,而不是导致它的原因。
答案 0 :(得分:7)
请参阅The F# 3.0 Language Specification (final version, PDF),§8.6.1类中的主要构造函数:
在构造期间,在类型中的最后一个值或函数定义之前,不能调用该类型的成员 已完成;这样的调用会导致 InvalidOperationException。
几乎可以肯定,问题中的代码并不能说明完整的故事。如果你碰到了上面的话 提到限制,然后在某处尝试访问未完全初始化的字段或成员。
一些例子:
type X() as this =
let x = this.X
member __.X = 42
X()
一种解决方法可能是将有问题的代码封装在自己的成员中,而是在构造函数中调用它。另一个是函数定义中的包装。
答案 1 :(得分:5)
这将是一个不完整的答案,因为我无法重现问题(使用F#interactive,给定示例,ReloadGoals修改和Form.Show
,代码运行正常)。然而,发生了奇怪的事情:
从模板中获取Form.Load event应该有一个处理程序方法,该方法在完全构造类型时触发。为什么构造函数中的其他加载代码而不是此事件处理程序? Load
正是为了解决这种无序初始化问题而存在的。
您使用的模板并非完全正确的F#。例如,initControls
是类型unit的值,在定义它的位置进行计算;它与名称的绑定绝对没用,应该用简单的do
替换。稍后在initControls
块中写do
根本没有效果。 form.ResumeLayout(false); form.PerformLayout()
应该等同于form.ResumeLayout(true)
,但我一开始并不了解这些在构造函数中的作用。事件处理程序有两个可能不必要的间接:一个是委托构造函数,另一个是没有真正理由存在的方法 - 处理程序应该是lambdas或简单的私有函数。他们为什么是公共会员?!
问题中出现的错误可能是由form
在其自己的构造函数中使用引起的。将新用法移至Load
事件处理程序,它应该可以正常工作。
就个人而言,我会更进一步,通过实例化一个普通的Form
并订阅它的事件来抛弃实现继承。例如,在FSI中,类似于模板的东西可以这样做:
open System.Drawing
open System.Windows.Forms
let form = new Form()
form.ClientSize <- new Size(600, 600)
form.Text <- "F# Form"
let formLabel = new Label()
formLabel.Text <- "Doubleclick test!"
formLabel.DoubleClick.Add <| fun _ -> form.Close()
form.Controls.Add(formLabel)
form.Show()
根本不使用继承。 (在应用程序中,您使用Application.Run
等而不是form.Show()
。)这不会很容易地遇到初始化问题,另外,如果要将表单封装在内部,则非常有用更简单的类型,甚至只是一个函数。