3

このサンプルコードをSOで見たところ、一方のプラクティスは悪く、もう一方のプラクティスは良いと言っていました。でもなぜか分かりませんか?実際のところ、私はその有名なRCW COMオブジェクトエラーを受け取っており、その投稿はこれが理由である可能性があると言っていました。

public class SomeClass
{
    private Interop.ComObjectWrapper comObject;
    private event ComEventHandler comEventHandler;

    public SomeClass()
    {
        comObject = new Interop.ComObjectWrapper();

        // NO - BAD!
        comObject.SomeEvent += new ComEventHandler(EventCallback);

        // YES - GOOD!
        comEventHandler = new ComEventHandler(EventCallback);
        comObject.SomeEvent += comEventHandler
    }

    public void EventCallback()
    {
        // DO WORK
    }

}

編集:ソースへのリンクは次のとおりです:基になるRCWから分離されたCOMオブジェクトは使用できません

4

1 に答える 1

4

これら 2 つのコード スニペットは同じだと思います。ここでは、強/弱参照に問題はありません。

バックグラウンド

まず第一に、Interop.ComObjectWrapperCLR イベント (つまり、イベント ハンドラーをデリゲートに格納するイベント) を提供する場合ComObjectWrapper、オブジェクトへの強力な参照を確実に取得します。

Targetすべてのデリゲートには、型objectと特定のメソッドへのメソッド ポインターの2 つの部分が含まれます。Targetコールバックがnull静的メソッドを指している場合。

TargetタイプWeakReferenceのデリゲートを持つことは不可能です。いわゆる弱いイベント パターンがありますが、プレーン デリゲートの代わりにEventManagerの上に実装されます。

イベント ハンドラをフィールドに格納しても役に立ちません。パート1

内部イベントの実装とは、イベントをサブスクライブした後に次のことを意味します。

comObject.SomeEvent += EventCallback;

comObjectobject は暗黙的にオブジェクトへの強い参照を保持しSomeClassます。これは、使用しているサブスクライブ手法の種類や、ComObject が COM オブジェクト ラッパーであるかどうかに関係なく当てはまります。

イベントをサブスクライブすると、有効期間に関して 2 つのオブジェクト間に暗黙的な依存関係が追加されます。そのため、.NET の世界で最も一般的なメモリ リークは、長寿命オブジェクトのイベントへのサブスクライブによって引き起こされます。イベント サブスクライバーは、イベント ホルダーがアプリケーションでアクセス可能になるまで終了しません。

イベント ハンドラをフィールドに格納しても役に立ちません。パート2

しかし、私の仮定が正しくなくComObjectWrapper、弱いイベント パターンの概念を提供する場合、イベント ハンドラーをフィールドに保存しても何の役にも立ちません。

イベントキーワードの意味を要約しましょう:

private event ComEventHandler comEventHandler;
... 
comEventHandler = new ComEventHandler(EventCallback);

コールバックを現在のフィールドに保存しても (基本的に、プライベート イベントを単純なデリゲート フィールドとして扱うことができます)、既存の動作は変わりません。

SomeClassデリゲートは、Target オブジェクト (オブジェクト) とメソッド (オブジェクト)への参照を格納する単純なオブジェクトであることは既にわかっていますpublic void EventCallBack()。これは、フィールドに追加のデリゲートを格納するとSomeClass、 自体からへの追加の参照が追加されることを意味しSomeClassます。

基本的に、フィールドにイベント ハンドラーを格納することは、追加の参照を SomeClass に格納することと意味的に同等です。

プライベート SomeClass someClass;

public SomeClaas() { // これは基本的にデリゲートを // comEventHandler フィールドに格納するのと同じです someClass = this; }

に強い参照を格納してSomeClassも、現在のオブジェクトの有効期間は延長されません。これは、イベント ハンドラーを格納するオブジェクトComObjectWrapperへの強い参照を保持しない場合、 SomeClass の有効期間を延長せず、ガベージ コレクションを妨げないことを意味します。SomeClasscomEventHandlerSomeClass

結論

イベント ハンドラーをプライベート フィールドに格納しても、オブジェクトの有効期間は延長されず、ガベージ コレクションも妨げられません。

そのため、オブジェクトの有効期間に関して、次のコード スニペットに違いはありません。

    // GOOD!
    comObject.SomeEvent += new ComEventHandler(EventCallback);

    // EVEN BETTER!
    comObject.SomeEvent += EventCallback;

    // NOT GOOD, BECAUSE WAN'T HELP!
    comEventHandler = new ComEventHandler(EventCallback);
    comObject.SomeEvent += comEventHandler
于 2012-11-27T10:16:00.310 に答える