强制使用mutable来读取Excel ThisWorkbook.Names?
我想获得一个基于F#的加载项的Excel命名范围中的数据。 我想能够在工作簿中的任何地方使用命名的范围,而不仅仅是一个特定的工作表。
let xlRange (xl:Microsoft.Office.Interop.Excel.Application, name:string) = let name_list = xl.ThisWorkbook.Names:Microsoft.Office.Interop.Excel.Names let mutable result=null for n in name_list do let nn = n :?> Microsoft.Office.Interop.Excel.Name if nn.Name = name then let range=nn.RefersToRange result <- range.Value2 :?> obj[,] result
上面的代码工作,但我不喜欢它,因为我不得不使用一个可变的,势在必行的风格。 问题是Excel.Names集合performance不佳。 例如
let name_seq = Seq.toList name_list
给
“名称”types与“seq <'a>”types不兼容
有没有更多的F#惯用的方式呢?
您应该使用Seq.cast
将name_list
为一个types化的序列。
优点是显而易见的; 你有更好的与F#的互操作性,可以使用Seq模块中的高阶函数,而不是循环的必要for
。
let xlRange (xl: Microsoft.Office.Interop.Excel.Application, name: string) = xl.ThisWorkbook.Names |> Seq.cast<Microsoft.Office.Interop.Excel.Name> |> Seq.tryPick (fun n -> if n.Name = name then Some (n.RefersToRange.Value2 :?> obj[,]) else None)
返回types现在是obj[,] option
,它为您提供免费的types安全。
我无法使用Seq。 直接因为不兼容的types。 我做了这个函数,目的是将集合转换为F#列表,然后我可以像上面那样处理。 它编译好,但我没有testing,因为…
let xlNamesToList names = let rec xlNameList_r (names:Microsoft.Office.Interop.Excel.Names) idx = if idx>names.Count then // should this be >= ? [] else names.Item(idx)::xlNameList_r names (idx+1) xlNameList_r names 0
…然后我find一个更简单的方法直接做到这一点:
let xlRange (xl:Microsoft.Office.Interop.Excel.Application, name:string) = try let r=xl.ThisWorkbook.Names.Item(name :> obj).RefersToRange Some(r.Value2 :?> obj[,]) with | _ -> None
谢谢你的帮助!