質問

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

void ReleaseObject(object comObject)

すべての comObject を解放した後、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 への逆キャストは暗黙的ではなく、メソッドが何をするかという保証はありません)、回避策はないのか、それとも何百もの null 代入を続ける必要があるのか​​疑問に思っていました。

注記:さまざまな COM オブジェクト タイプが多数あるため、タイプ セーフなパラメーターではなく、「オブジェクト」パラメーターが必要です。

役に立ちましたか?

解決

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

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

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

(コメントでの議論の後。)

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

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

他のヒント

Sunny、ref、out はマーシャリングのヒントとコンパイラへの契約です。Ref と out は COM 時代に引き継がれます。これは、ネットワーク上またはプロセス間で送信されるときのオブジェクトのマーシャリング ヒントです。

out 契約

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

ref 契約

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

最後の 2 つの点の現実は、やろうとしていることの実行を妨げる可能性があります。参照が実際に何であるかを理解している場合、実際にはそれらは意味をなさないからです。それを知りたければ、ジョン・スキートに聞いてください(彼は本を書きました)。

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


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

他の人が指摘しているように、何か怪しいことが起こっています。あなたが保守しているブルートフォースコードにはいくつかの微妙なバグがあり、偶然のコーディングに影響を受けているようです。最善の解決策はおそらく、別の間接層を追加することです。つまりラッパー クラスへのラッパー。確定的なクリーンアップを保証します。コードベース全体にコードを散りばめるのではなく、面倒なコードを 1 回だけ記述できます。


そうは言っても..

代替案 1

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

// 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 に設定する必要があることを前提としていますが、これは (通常は) 必要ありません。

私の意見では、別のメソッドでこれらのオブジェクトを null に設定することはできません (ところで、次のメソッドを使用する必要があります) 参照 パラメータの代わりに とにかくそれを機能させるために、あなたは「変換できない...」エラーと同じ問題にヒットするでしょう。 。何かのようなもの:

List garbagedObjects = new List();
garbagedObjects.Add(myComObject1);
garbagedObjects.Add(myComObject2);
...
foreach(object garbagedObject in garbagedObjects)
{
  ReleaseObject(garbagedObject);
  garbagedObject = null;
}
garbagedObjects = null;
GC.Collect();
...

あなたが電話するはずです Marshal.ReleaseComObject, 、私の知る限り、これは1.1で利用可能でした。

おそらく「ref」のことを意味していると思われます。

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);
}
ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top