Ale*_*ett 4 f# invalidoperationexception winforms
所以我用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.
Run Code Online (Sandbox Code Playgroud)
代码是从表单本身的构造函数调用的成员方法
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
Run Code Online (Sandbox Code Playgroud)
我抓住我正在使用的项目的模板的网站就是这个.不幸的是,我已经为此做了一些实质性的补充.
我很乐意发布更多代码,但我需要知道哪些代码完全相关,因为我不完全确定并且不想在无关代码中陷入困境.
另外,我无法在System.InvalidOperationException上找到很多文档.每次我找到它,它被用作一个例子例外的,你可以扔在你自己的,不是什么原因造成的.
请参阅F#3.0语言规范(最终版本,PDF),§8.6.1类中的主要构造函数:
在构造期间,在类型中的最后一个值或函数定义完成之前,不能调用该类型的成员; 这样的调用会导致InvalidOperationException.
几乎可以肯定,问题中的代码并不能说明完整的故事.如果你达到了上述限制,那么就会尝试访问未完全初始化的字段或成员.
一些例子:
type X() as this =
let x = this.X
member __.X = 42
X()
Run Code Online (Sandbox Code Playgroud)
一种解决方法可能是将违规代码封装在自己的成员中,并在构造函数中调用它.另一个是函数定义中的包装.
这将是一个不完整的答案,因为我无法重现问题(使用F#interactive,给定的示例,ReloadGoals修改,并且Form.Show代码运行正常).然而,发生了奇怪的事情:
从模板中获取,应该有一个Form.Load事件的处理程序方法,该方法在完全构造类型时触发.为什么构造函数中的其他加载代码而不是此事件处理程序?Load正是为了解决这种无序初始化问题.
你正在使用的模板并不完全是理智的F#.例如,initControls是在定义它的地方计算的单位类型的值; 它与名称的绑定绝对没用,应该用简单的替换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()
Run Code Online (Sandbox Code Playgroud)
它根本不使用继承.(在应用程序中,您将使用Application.Runetc而不是form.Show().)这不会很容易地遇到初始化问题,另外,如果您希望将表单封装在更简单的类型甚至只是函数中,这将非常有用.