2016-08-18 5 views
4

こんにちは、これはC#言語がCom interopリソース管理をどのように処理するかの例です。 orginal sourceF#でリソースを処理する機能的な方法

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); 
} 

は個人的に私は上記のコードは、合理的かつ必要だと思います。しかし、それは機能的ではありません。私はこれらのcom変数を、try ... finally ...とtry ... ...の異なるレベルで定義してしまいました。そしてtryブロックの前に変数を定義しなければならないので、finallyコードとwithブロックの両方にクリーンアップコードが存在します。それは非常に面倒です。

F#で同じことを正しく実装できますか?その少し皮肉なことに、F#を実証する方法として、interopでF#を使用する方法を説明しているインターネット上の多くの例があります。しかし、誰もcomリソースのクリーンアップを管理する方法については話しません。

良いパターンに関するアドバイスがあります。

+3

私の携帯電話は、IDisposableオブジェクト(またはComがIDisposableを実装していない場合はラッパー)を使用し、F#の 'use'式を使用しているので、簡単に問題を解決できます。 。オブジェクトがスコープ外に出たときにDispose()を呼び出す点を除いて 'let'と似ています。 – rmunn

答えて

6

try/catchの各ステップを呼び出し、最後に完了したときに解放する計算式を作成できます。私たちは、作成/ファイナライズのためのプラグイン関数を持つビルダーを作成して、何が起こっているかを見ることができます。

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" 

これのどれもがとにかく必要ありませんように見える、と述べているすべて。 https://stackoverflow.com/a/25135685/171121

+0

こんにちはダックス、私は今すぐコードをテストすることはできませんが、私は頼むことができます! z = failwith "ブーム"コードは、x + y + zの実行中ではないzの作成中に失敗しました。つまり、プログラムがinterop excelオブジェクトを使用して何かを行う場合、letバインディングを使用してロジックのすべての行を書き込む必要がありますか? x + y + zで例外が発生し、それがエラーハンドルとバインディングをバインドする際にラップされていない場合、適用ロジックは適用されませんか? – casbby

+1

@casbby nopeでは、計算式はその中のどこかで発生する例外を処理し、リリースロジックを適用します。したがって、最初の例を 'return x + y + z +(failwith" boom ")'に変更すると、これまでに作成したすべてのものを解放して(x、y、z)、 'Choice2Of2" boom "を返します。 –

+0

確かに! letの各レベルに対して、最終的にネストされたtry ..with ...を作成します!手動で定義することを避けています。このような迅速な対応に非常に感謝しています。もう1つの質問。私はゼロを完全に理解していません。一般的に、そしてこの場合、その目的は何ですか? zero()が指定されていても、あなたのコードは動作します。 – casbby

関連する問題