2008-10-28 38 views
2

我堅持使用.net 1.1應用程序(即我現在不能使用2.0的泛型好東西),並且我試圖優化某些部分碼。由於它處理了很多需要發佈的運行時可調用包裝器,因此我最終創建了一個實用程序方法,該方法循環直到所有引用都被釋放。該方法的簽名是:使用「ref」和/或「out」對象類型

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

這將失敗並顯示一條消息:「無法從'MyComClass'轉換爲'out object'」

雖然我可以想到它爲什麼會成爲問題(即,從對象到MyComClass的反向投射不是隱含的,並且不能保證該方法會做什麼),我想知道是否有解決方法,或者我需要保留我的數百個空值任務。

注:我有一堆不同的COM對象類型,這就是爲什麼我需要一個「對象」參數,而不是一個類型安全的。

回答

2

爲什麼調用方法比只將變量設置爲null更好?他們都是單線電話,後者要簡單得多。

這聽起來很奇怪,你需要將它們設置爲null,儘管如此。這些靜態變量或實例變量的值是否需要早於其包含對象才能釋放?如果變量只是一個本地變量,它將會超出範圍,將其設置爲null應該不會有任何區別(在發佈中)。

RCWs是否未實現IDisposable?如果是這樣,調用Dispose(最好通過使用語句)將是最好的選擇。

(後在評論中討論。)

這些是局部變量,這是不是在方法以後引用。這意味着垃圾收集器會意識到它們不需要被視爲「根」引用 - 因此將它們設置爲null應該沒有任何區別。

直接回答原來的問題,但是:不,你不能引用,除非該方法的參數是完全相同的類型傳遞變量,所以你的運氣了這裏。 (有了泛型這將是可能的,但你說你是侷限於.NET 1.1)。

+0

@Jon:= null重複操作 - 這個重構:); RCW不是IDisposable;它是Office interop特定的,我需要在範圍結束前強制GC。正如我所說,我可以保持原樣,但我很好奇是否有辦法使用「out」來做到這一點。 – 2008-10-28 21:04:47

+0

你會重構「重複設置變量爲空」到「重複製作方法調用」 - 哪裏有好處? 至於是否需要這樣做 - 將變量設置爲null不會強制GC。發生了什麼(在發佈模式下,不在調試器下,重要)......(續) – 2008-10-28 21:30:26

+0

如果您調用GC.Collect()而不將其設置爲null?如果它們是局部變量,那應該沒問題。 – 2008-10-28 21:31:09

0

你應該叫Marshal.ReleaseComObject,這AFAIK在1.1可用。

你大概的意思是「參考」:

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

[編輯再評論]不過,這隻適用於非類型化對象的工作,所以沒有仿製藥多用!哦,對於C#2.0 ...

重新「ref」;如果變量是真正的變量(意思是:方法變量),那麼它們很快就會超出範圍並被收集。 「ref」只會用於釋放字段。但說實話,這將是簡單的只是將它們設置爲null ...

一個典型的COM模式是:

SomeType obj = new SomeType(); 
try { 
    obj.SomeMethod(); // etc 
} finally { 
    Marshal.ReleaseComObject(obj); 
} 
0

在我看來,你將無法設置這些對象爲null在另一種方法(順便說一句,你將需要使用編號參數,而不是使其工作,無論如何,你會遇到「無法轉換...」錯誤相同的問題。) 我建議創建和而不是遍歷該數組,調用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(); 
... 
3

Sunny,ref和out是一個編組提示+合同到編譯器。 Ref和Out是對COM日的結轉 - 通過線/進程之間發送的對象的編組提示。

out合同

void foo(out MyClass x) 
  1. 富()將設置x的東西它返回之前。
  2. x輸入foo()時沒有任何價值,如果您在設置之前嘗試使用x,則會出現編譯器錯誤。 (使用未分配出參數x)

ref合同

void foo(ref MyClass x) 
  1. REF允許改變所述呼叫者參考。
  2. x具有可分配
    • 你不能投射東西一箇中間變量foo的(參考文獻(對象)的東西)
    • x不能是一個屬性

的現實最後兩點可能會阻止你做你想做的事情,因爲實際上,當你明白什麼是真正的參考時,他們沒有任何意義。如果你想知道這一點,可以問Jon Skeet(他寫了一本書)。

編組ref時,它說除了返回值之外,還帶回ref值。 編組時,它表示在調用該方法時不要打擾輸出值,但記住除了返回值外還要帶出輸出值。


免責聲明免責聲明

正如其他人所指出的,有鬼是怎麼回事。看起來你正在維護的暴力代碼有一些微妙的錯誤,並且因巧合編碼而受到影響。最好的解決方案可能是添加另一個間接層。即包裝類的包裝,確保確定性的清理,你可以一次又一次地編寫混亂的代碼,而不是在整個代碼庫中使用它。


這就是說..

替代,除非你爲每個類型(COM)的重載對象你會稱它爲1

價不會做的伎倆。

// 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()方法無效,並強制轉換回對象的類型。

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)調用時沒有賦值(使錯誤的代碼看起來錯誤),就會變得可疑。你甚至可以通過代碼尋找調用來刪除不發生賦值的地方。


聲明 - 所有上述的前提是您需要設置參考手動null,它(通常)是沒有必要的。