首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >F#中处理资源清理的函数方式

F#中处理资源清理的函数方式
EN

Stack Overflow用户
提问于 2016-08-18 08:55:54
回答 1查看 153关注 0票数 4

你好,这是C#语言如何处理Com资源管理的一个例子。orginal source

代码语言:javascript
复制
Excel.Application app = null;
Excel.Workbooks books = null;
Excel.Workbook book = null;
Excel.Sheets sheets = null;
Excel.Worksheet sheet = null;
Excel.Range range = null;

try
{
    app = new Excel.Application();
    books = app.Workbooks;
    book = books.Add();
    sheets = book.Sheets;
    sheet = sheets.Add();
    range = sheet.Range["A1"];
    range.Value = "Lorem Ipsum";
    book.SaveAs(@"C:\Temp\ExcelBook" + DateTime.Now.Millisecond + ".xlsx");
    book.Close();
    app.Quit();
}
finally
{
    if (range != null) Marshal.ReleaseComObject(range);
    if (sheet != null) Marshal.ReleaseComObject(sheet);
    if (sheets != null) Marshal.ReleaseComObject(sheets);
    if (book != null) Marshal.ReleaseComObject(book);
    if (books != null) Marshal.ReleaseComObject(books);
    if (app != null) Marshal.ReleaseComObject(app);
} 

就我个人而言,我认为上面的代码是合理和必要的。但它不是函数式的或F#方式。我最终在不同级别的嵌套try中定义了所有这些com变量……finally和try...with,因为变量必须在try块之前定义,所以清理代码同时存在于finally和with块中。它非常凌乱。

怎样才能在F#中正确地实现相同的功能呢?有点讽刺的是,互联网上有很多例子解释了如何通过互操作来使用F#来展示F#的力量。但是,它们都不能转换如何管理com资源清理。

任何关于好模式的建议都会得到重视。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2016-08-18 11:50:45

您可以创建一个计算表达式,该表达式调用try/catch中的每个步骤,并在完成时在finally中释放。我们可以为create/finalize创建一个带有插件函数的构建器,这样我们就可以看到发生了什么。

代码语言:javascript
复制
type FinalizationBuilder(oncreate, onfinal) =
  member __.Bind(m, f) = 
    oncreate(box m)
    try
      try
        f m
      with ex ->
        Choice2Of2 ex.Message
    finally
      onfinal(box m)
  member __.Return(m) = Choice1Of2 m
  member __.Zero() = Choice1Of2()

然后,您需要一个COM工作流,它只在COM组件完成时释放它。

代码语言:javascript
复制
let com = new FinalizationBuilder(ignore, System.Runtime.InteropServices.Marshal.ReleaseComObject >> ignore)

您可以这样使用它:

代码语言:javascript
复制
[<EntryPoint>]
let main _ = 
  com { 
    let! app = new Excel.Application()
    let! books = app.Workbooks
    let! book = books.Add()
    // ...
    app.Quit()
  } |> ignore
  0

我没有安装excel,但我可以用exceptions和printfns来模拟它。

代码语言:javascript
复制
let demo = new FinalizationBuilder(printfn "Created %A", printfn "Released %A")

[<EntryPoint>]
let main _ = 
  demo { 
    let! x = 1
    let! y = 2
    let! z = 3
    return x + y + z
  } |> printfn "Result: %A"
  0

// Created 1
// Created 2
// Created 3
// Released 3
// Released 2
// Released 1
// Result: Choice1Of2 6

或者有例外:

代码语言:javascript
复制
[<EntryPoint>]
let main _ = 
  demo { 
    let! x = 1
    let! y = 2
    let! z = failwith "boom"
    return x + y + z
  } |> printfn "Result: %A"
  0

// Created 1
// Created 2
// Released 2
// Released 1
// Result: Choice2Of2 "boom"

综上所述,看起来这些都不是必须的。一个简单的GC.Collect(); GC.WaitForPendingFinalizers()就可以解决这个问题,而不需要任何这些:https://stackoverflow.com/a/25135685/171121

票数 6
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/39008385

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档