在 F# 中处理资源清理的功能方法
Functional way of handling resource clean up in F#
您好,这是 C# 语言如何处理 Com 互操作资源管理的示例。 orginal source :
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...finally 和 try...with 中定义了所有这些 com 变量,因为变量必须在 try 块之前定义,所以清理代码存在于 finally 和 with 块中。这是非常混乱的。
如何在 F# 中正确实现相同的功能?有点讽刺的是,互联网上有很多示例解释了如何将 F# 与互操作结合使用,以此来展示 F# 的强大功能。然而,其中 none 讨论了如何管理 com 资源清理。
欢迎任何关于好的模式的建议。
您可以创建一个计算表达式,在 try/catch 中调用每个步骤,并在完成后在 finally 中释放。我们可以为 create/finalize 创建一个带有插件函数的构建器,这样我们就可以看到发生了什么。
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 组件。
let com = new FinalizationBuilder(ignore, System.Runtime.InteropServices.Marshal.ReleaseComObject >> ignore)
你这样使用它:
[<EntryPoint>]
let main _ =
com {
let! app = new Excel.Application()
let! books = app.Workbooks
let! book = books.Add()
// ...
app.Quit()
} |> ignore
0
我没有安装excel,但我可以用异常和printfns模拟它。
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
或有例外:
[<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"
综上所述,看来 none 无论如何都是必要的。一个简单的 GC.Collect(); GC.WaitForPendingFinalizers()
可以解决这个问题,而不需要任何这些:
您好,这是 C# 语言如何处理 Com 互操作资源管理的示例。 orginal source :
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...finally 和 try...with 中定义了所有这些 com 变量,因为变量必须在 try 块之前定义,所以清理代码存在于 finally 和 with 块中。这是非常混乱的。
如何在 F# 中正确实现相同的功能?有点讽刺的是,互联网上有很多示例解释了如何将 F# 与互操作结合使用,以此来展示 F# 的强大功能。然而,其中 none 讨论了如何管理 com 资源清理。
欢迎任何关于好的模式的建议。
您可以创建一个计算表达式,在 try/catch 中调用每个步骤,并在完成后在 finally 中释放。我们可以为 create/finalize 创建一个带有插件函数的构建器,这样我们就可以看到发生了什么。
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 组件。
let com = new FinalizationBuilder(ignore, System.Runtime.InteropServices.Marshal.ReleaseComObject >> ignore)
你这样使用它:
[<EntryPoint>]
let main _ =
com {
let! app = new Excel.Application()
let! books = app.Workbooks
let! book = books.Add()
// ...
app.Quit()
} |> ignore
0
我没有安装excel,但我可以用异常和printfns模拟它。
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
或有例外:
[<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"
综上所述,看来 none 无论如何都是必要的。一个简单的 GC.Collect(); GC.WaitForPendingFinalizers()
可以解决这个问题,而不需要任何这些: