1

我正在尝试获取基于 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 集合表现不佳。例如

let name_seq = Seq.toList name_list

'Names' 类型与 'seq<'a>' 类型不兼容

有没有更 F# 惯用的方式来做到这一点?

4

2 回答 2

1

您应该使用Seq.casttoname_list转换为类型化序列。

优势明显;您与 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)

现在的返回类型是obj[,] option免费为您提供类型安全的。

于 2012-07-07T19:43:34.983 回答
0

我无法使用 Seq。事情直接因为不兼容的类型。我制作了这个函数,旨在将集合转换为 F# 列表,然后我可以按上述方式处理该列表。它编译得很好,但我没有测试,因为......

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

...然后我找到了一种更简单的直接方法:

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

谢谢你们的帮助!

于 2012-07-09T11:19:24.727 回答