2

私は .Net 1.1 アプリケーションに行き詰まっており (つまり、今のところ 2.0 のジェネリック機能を使用できません)、コードの一部を最適化しようとしていました。リリースする必要があるランタイム呼び出し可能なラッパーを多く扱うため、すべての参照がリリースされるまでループするユーティリティ メソッドを作成することになりました。メソッドのシグネチャは次のとおりです。

void ReleaseObject(object comObject)

すべての comObjects を解放した後、GC.Collect と GC.WaitForPendingFinalizers を呼び出します (質問しないでください - Office の相互運用を扱っている人なら誰でも知っています)。

そして ... いつものように、まれなケースに遭遇します。GC.Collect 呼び出しの前に、対応するマネージド参照を null に割り当てないと、適切にクリーンアップされません。

したがって、私のコードは次のようになります。

ReleaseObject(myComObject);
myComObject = null;
GC.Collect()
...

xxx=null がたくさんあるので、これを util メソッドに入れることにしましたが、参照渡しと参照パラメーターの受け渡しには違いがあるため、明らかにメソッドを次のように変更する必要がありました。

void ReleaseObject(out object comObject)
{
   //do release
   comObject = null;
}

呼び出し元を次のように編集します。

MyComClass myComObject = xxxx;
ReleaseObject(out myComObject);

これは、「'out MyComClass' から 'out object' に変換できません」というメッセージで失敗します。

なぜそれが問題になるのか (つまり、オブジェクトから MyComClass へのリバース キャストは暗黙的ではなく、メソッドが何を行うか保証されていない) を考えることはできますが、回避策があるかどうかは疑問に思っていました。私の何百ものヌルの割り当てで。

注: 私はさまざまな COM オブジェクト タイプを持っているため、タイプ セーフではなく「オブジェクト」パラメータが必要です。

4

4 に答える 4

3

Sunny、ref、outは、マーシャリングのヒントとコンパイラーとの契約です。Refとoutは、COM日への持ち越しです。これは、ネットワークを介して/プロセス間で送信される場合のオブジェクトのマーシャリングのヒントです。

out契約_

void foo( out MyClass x)
  1. foo()はx、戻る前に何かに設定されます。
  2. xxfoo()が入力されたときは値がなく、設定する前に使用しようとするとコンパイラエラーが発生します。(割り当てられていない出力パラメーターxの使用)

ref契約_

void foo( ref MyClass x)
  1. refを使用すると、呼び出し元の参照を変更できます。
  2. xは割り当て可能である必要があります
    • 中間変数に何かをキャストすることはできませんfoo(ref(object)something)
    • xをプロパティにすることはできません

最後の2つのポイントの現実は、実際には参照が実際に何であるかを理解するときに意味をなさないため、あなたがやろうとしていることをやめさせる可能性があります。それを知りたい場合は、Jon Skeet(彼は本を書いた)に聞いてください。

refをマーシャリングする場合、戻り値に加えて、ref値も戻すように指示されています。マーシャリングするときは、メソッドが呼び出されたときにout値を送信する必要はありませんが、戻り値に加えてout値を戻すことを忘れないでください。


免責事項免責事項免責事項

他の人が指摘するように、何か怪しいことが起こっています。あなたが維持しているブルートフォースコードにはいくつかの微妙なバグがあり、偶然の一致によるコーディングに苦しんでいるようです。最善の解決策は、おそらく間接参照の別のレイヤーを追加することです。つまり、ラッパークラスのラッパーであり、コードベース全体にペッパーを付ける代わりに、乱雑なコードを1回だけ記述できる決定論的なクリーンアップを保証します。


それは言った..

代替案1

Refは、呼び出すすべてのタイプの(com)オブジェクトにオーバーロードを提供しない限り、このトリックを実行しません。

// need a remove method for each type. 
void Remove( ref Com1 x ) { ...; x = null; }
void Remove( ref Con2 x ) { ...; x = null; }
void Remove( ref Com3 x ) { ...; x = null; }

// a generics version using ref.
void RemoveComRef<ComT>(ref ComT t) where ComT : class
{
    System.Runtime.InteropServices.Marshal.ReleaseComObject(t);
    t = null; 
}

Com1 c1 = new Com1();
Com2 c2 = new Com2();
Remove( ref c1 );
RemoveComRef(ref c2); // the generics version again.

代替案2

それを望まない場合は、Remove()メソッドからnullを返し、オブジェクトの型にキャストバックします。

class Remover
{
    // .net 1.1 must cast if assigning
    public static object Remove(object x)
    {
        System.Runtime.InteropServices.Marshal.ReleaseComObject(x);
        return null;
    }

    // uses generics.
    public static ComT RemoveCom<ComT>(ComT t) where ComT : class
    {
        System.Runtime.InteropServices.Marshal.ReleaseComObject(t);
        return null;
    }   
}

Com1 c1 = new Com1();
Com2 c2 = new Com2();
c1 = (Com1)Remover.Remove(c1); // no reliance on generics
c2 = Remover.RemoveCom(c2); // relies on generics

*比較のために汎用バージョンを追加しました。

上記のコードには、コードを見るときに、割り当てなしでRemove(x)の呼び出しを見ると、疑わしいものになるという効果があります(間違ったコードが間違って見えるようになります)。コードベースを介してGrepを実行し、割り当てが行われない場所でRemoveの呼び出しを探すこともできます。


免責事項-上記のすべては、参照を手動でnullに設定する必要があることを前提としていますが、これは(通常は)必要ありません。

于 2008-10-29T00:18:52.640 に答える
3

変数を単に null に設定するよりも、メソッドを呼び出すほうがよいのはなぜですか? どちらも 1 行の呼び出しで、後者の方がはるかに簡単です。

ただし、最初にそれらを null に設定する必要があるのは非常に奇妙に思えます。これらは静的変数ですか、それとも含まれるオブジェクトよりも先に値を解放する必要があるインスタンス変数ですか? 変数がスコープ外になる単なるローカル変数である場合、null に設定しても (リリースでは) 違いはありません。

RCW は IDisposable を実装していませんか? その場合は、Dispose を (できれば using ステートメントを介して) 呼び出すのが最善の方法です。

(コメントで議論した後。)

これらはローカル変数であり、メソッドの後半では参照されません。つまり、ガベージ コレクターは、"ルート" 参照として扱う必要がないことを認識します。そのため、それらを null に設定しても違いはありません。

ただし、元の質問に直接答えるには: いいえ、メソッドのパラメーターがまったく同じ型でない限り、参照によって変数を渡すことはできないため、ここでは運が悪いです。(ジェネリックでは可能ですが、.NET 1.1 に限定されているとのことでした。)

于 2008-10-28T20:54:29.840 に答える
0

AFAIKMarshal.ReleaseComObjectは 1.1 で利用可能でした。

あなたはおそらく「参照」を意味します:

static void ReleaseObject(ref object comObject)
{
   if(comObject != null)
   {
     //do release
     comObject = null;
   }
}

[コメントを編集] ただし、これは型指定されていないオブジェクトに対してのみ機能するため、ジェネリックなしではあまり使用できません! ああ、C# 2.0 の場合...

「参照」について。変数が真の変数 (メソッド変数の意味) である場合、それらはすぐに範囲外になり、収集されます。「ref」は、フィールドを解放する場合にのみ役立ちます。しかし、正直なところ、それらを null に設定するだけの方が簡単です...

典型的な COM パターンは次のとおりです。

SomeType obj = new SomeType();
try {
  obj.SomeMethod(); // etc
} finally {
  Marshal.ReleaseComObject(obj);
}
于 2008-10-28T21:08:12.277 に答える
0

私の意見では、これらのオブジェクトを別のメソッドで null に設定することはできません (ところで、それを機能させるにはoutの代わりにrefパラメータを使用する必要があります。とにかく、「変換できません...」で同じ問題が発生します。エラーです。) オブジェクトの配列を作成してから、その配列を反復処理し、ReleaseObject メソッドを呼び出してそれらのオブジェクトを null に設定することをお勧めします。何かのようなもの:

List garbagedObjects = new List();
garbagedObjects.Add(myComObject1);
garbagedObjects.Add(myComObject2);
...
foreach(object garbagedObject in garbagedObjects)
{
  ReleaseObject(garbagedObject);
  garbagedObject = null;
}
garbagedObjects = null;
GC.Collect();
...
于 2008-10-28T21:12:26.730 に答える