F#类中Dictionary操作异常求助:添加元素后仍报KeyNotFoundException
F#类中Dictionary赋值异常分析
我近期开始学习F#,并在个人项目中尝试用多种语言实现康威生命游戏,目前遇到类内Dictionary赋值的问题。
问题代码
module World open System.Collections.Generic type World() = member private this.cells = new Dictionary<string, int>() member this.add_cell(): int = printfn "%A" this.cells this.cells.Add("0-0", 123) printfn "%A" this.cells this.cells["0-0"] let world = new World() world.add_cell() |> ignore
运行输出
seq [] seq [] Unhandled exception. System.Collections.Generic.KeyNotFoundException: The given key '0-0' was not present in the dictionary. at System.Collections.Generic.Dictionary`2.get_Item(TKey key) at World.World.add_cell()
异常现象
- 本应为Dictionary的
this.cells打印显示为seq [] - 添加元素后打印
this.cells仍为空 - 获取刚添加的键值时触发
KeyNotFoundException
原因分析与解决方法
核心问题是你把cells定义成了计算型成员,而非类的私有字段。在F#中,member private this.cells = new Dictionary<string, int>()这种写法会让每次访问this.cells时,都重新创建一个全新的Dictionary实例:
- 两次打印
this.cells时,都是生成新的空字典,所以显示为seq [](Dictionary实现了IEnumerable,%A格式化会按序列输出) - 调用
this.cells.Add是给临时生成的字典加元素,操作完成后这个临时字典就被丢弃了 - 最后访问
this.cells["0-0"]时,又创建了新的空字典,自然找不到对应的键
要解决这个问题,需要把cells定义为类实例的私有字段,确保整个实例生命周期内只初始化一次:
方法一:使用构造函数绑定字段(推荐)
module World open System.Collections.Generic type World() = // 定义私有字段,仅在实例创建时初始化一次 let cells = new Dictionary<string, int>() member this.add_cell(): int = printfn "%A" cells cells.Add("0-0", 123) printfn "%A" cells cells["0-0"] let world = new World() world.add_cell() |> ignore
方法二:使用val关键字定义字段
module World open System.Collections.Generic type World() = // 声明私有字段 [<DefaultValue>] val private cells : Dictionary<string, int> // 在构造函数代码块中初始化字段 do this.cells <- new Dictionary<string, int>() member this.add_cell(): int = printfn "%A" this.cells this.cells.Add("0-0", 123) printfn "%A" this.cells this.cells["0-0"] let world = new World() world.add_cell() |> ignore
两种写法都能保证所有操作针对同一个Dictionary实例,运行后就能正常输出添加的键值对,不会再抛出异常。
内容的提问来源于stack exchange,提问作者Kieran Pilkington
相关产品推荐
相关产品推荐

