【问题标题】:InvalidOperationException in Fsharp.Core.dllFsharp.Core.dll 中的 InvalidOperationException
【发布时间】:2014-12-18 11:19:05
【问题描述】:

所以我正在用 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 的文档。 每次我找到它时,它都被用作异常you can throw on your ownan example,而不是导致它的原因。

【问题讨论】:

  • 你会遇到这个问题吗? msdn.microsoft.com/en-us/library/ms182331.aspx
  • 我很欣赏这个想法。我没有从构造函数调用的任何虚拟方法。它们都已实施。即使有些是以 () 作为方法主体的占位符。
  • 在构造函数代码中,您是否将form 传递给您类型的任何其他构造函数?这可能会导致您看到的异常。

标签: winforms f# invalidoperationexception


【解决方案1】:

参见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()

一种解决方法可能是将有问题的代码封装在它自己的成员中,然后在构造函数中调用它。另一个是函数定义中的包装。

【讨论】:

  • 这就是问题所在。不确定在这种情况下封装是什么意思。由于必须设置 F# 类型的方式,在构造函数完成之前似乎没有任何成员会被完全加载,将其分割在另一个成员中并从构造函数运行它似乎首先是问题所在。
  • @AlexanderRyanBaggett 只要在last value or function definition之后调用成员,就可以了。
【解决方案2】:

这将是一个不完整的答案,因为我无法重现该问题(使用 F# 交互式、给定示例、ReloadGoals 修改和Form.Show,代码运行良好)。但是,发生了一些奇怪的事情:

  • 取自模板,Form.Load event 应该有一个处理方法,该方法在类型完全构造时触发。为什么在构造函数而不是这个事件处理程序中附加加载代码? Load 的存在正是为了解决这种无序初始化的问题。

  • 您使用的模板不是完全正常的 F#。例如,initControls 是一个 unit 类型的值,它在定义的地方进行评估;它与名称的绑定是绝对没用的,应该用简单的do 替换。稍后在do 块中写入initControls 完全没有效果。 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()。)这不会很容易遇到初始化问题,此外,如果您想将表单封装在更简单的类型中,或者甚至只是一个函数。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-08-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-12-11
    相关资源
    最近更新 更多