2009-07-28 12 views
5

Moja klasa zawiera obiekt z Interop i wywołuje na nim metodę, która powoduje przydzielanie rzeczy. naraża to także sposób, aby uwolnić te rzeczy, więc spodziewam powinienem nazwać to w Dispose():Jak zaimplementować wzorzec dispose w języku C# podczas owijania obiektu COM Interop?

class MyClass : IDisposable 
{ 
    private DllName.ComClassName comInstance; 

    void SomeMethod() 
    { 
     comInstance = new DllName.ComClassName(); 
     comInstance.AllocStuff(); 
    } 

    public void Dispose() 
    { 
     comInstance.FreeThatStuff(); 
    } 
} 

Teraz muszę poszerzyć wszystko, aby podążać za Dispose wzór. Nie mam żadnych innych środków jednorazowych lub niezarządzalny do wydania, więc zakładając comInstance jest zarządzany (nie jest to, co robi Interop, okłady niekontrolowana się udało?), Myślę, że disolves wzorzec:

public void Dispose() 
{ 
    if (comInstance != null) 
    { 
     comInstance.FreeStuff(); 
     comInstance = null; 
    } 
} 

z wyciekiem, chyba że jawnie wywołać Dispose() na instancjach MyClass, co spowodowałoby wadliwy wzór Dispose? Więc to znaczy comInstance musi być niekontrolowana, a wzór disolves do:

public void Dispose() 
{ 
    DisposeComInstance(); 
    GC.SuppressFinalize(this); 
} 

~MyClass() 
{ 
    DisposeComInstance(); 
} 

private void DisposeComInstance() 
{ 
    if (comInstance != null) 
    { 
     comInstance.FreeStuff(); 
     comInstance = null; 
    } 
} 

EDIT:

  1. Aby uniknąć zaśmiecania moją klasę z pełnym wzorem, może po prostu zamknąć moją klasę?
  2. Skąd wiadomo, że ComClassName (i ogólnie każda klasa) jest niezarządzana?

Odpowiedz

2

Wygląda na to, że prawie zostałeś przybity. Wracam do schematu, w którym masz chronione wirtualne usuwanie, które przyjmuje parametr boolowski wskazujący, czy zarządzane produkty powinny zostać usunięte. W ten sposób ktoś, kto będzie za Tobą, będzie nadal implement IDisposable properly.

public void Dispose() 
{ 
    this.Dispose(true); 
    GC.SuppressFinalize(this); 
} 

~MyClass() 
{ 
    this.Dispose(false); 
} 

protected virtual void Dispose(bool disposing) 
{ 
    // if (disposing) 
    // { 
    //  // Managed 
    // } 

    if (comInstance != null) 
    { 
     comInstance.FreeStuff(); 
     comInstance = null; 
    } 

    // base.Dispose(disposing) if required 
} 
+1

Meta-komentarz: upewnij się, że Twój Dispose (bool) może znieść wielokrotne wywoływanie. Spróbuj także zminimalizować możliwość wyrzucania wyjątku (nie zawsze jest to możliwe). – user7116

+1

Meta-meta comment - a także, że ważne jest, aby nigdy nie zdobywać blokad ani nie używać blokady podczas niezarządzanego czyszczenia. – womp

3

Ostatecznie chcesz tego rodzaju wzoru:

public void Dispose() 
{ 
    Dispose(true); 
    GC.SuppressFinalize(this); 
} 

~MyClass() 
{ 
    Dispose(false); 
} 

private void Dispose(bool disposing) 
{ 
    if (disposing) 
    { 
     // Dispose of disposable objects here 

    } 

    // Other unmanaged cleanup here which will be called by the finalizer 
    if (comInstance != null) 
    { 
     comInstance.FreeStuff(); 
     comInstance = null; 
    } 

    // Call base dispose if inheriting from IDisposable class. 
    base.Dispose(true); 
} 

na wielki artykuł na temat dlaczego, sprawdź Implementing IDisposable and the Dispose pattern properly.

+0

-1, Musisz przenieść niezarządzane obiekty poza blokiem if (disposing). Niezarządzanego należy zawsze czyścić. – user7116

+0

Gah! Masz rację, skopiowałem go z mojego kodu, który korzystał z komponentów, które miały niezarządzany kod, ale zaimplementowałem utylizację i edytowałem w przykładzie OP ... wkleić do utraty. Naprawiony. – womp

+0

+1, poprawnie zaimplementowane ;-D – user7116

3

Po pierwsze, zgadzam się z tymi, które sugerują, posiadające finalizatora jako backup , ale staraj się unikać wywoływania tego podczas wywoływania jawnie myClass.Dispose lub przez "używanie".

np.

var myClass = new MyClass() 
try 
{ 
    //do stuff 
} 
finally 
{ 
    myClass.Dispose(); 
} 

lub

using (var myClass = new MyClass()) 
{ 
    //do stuff 
} 

Marshall.ReleaseComObject

Jeśli używasz wiele obiektów COM, ja również proponuję użyć Mashall.ReleaseComObject (comObj) jawnie oczyścić odniesień do RCW.

więc kod jak to zasugerował gdzie indziej:

if (comInstance != null) 
{ 
    comInstance.FreeStuff(); 
    comInstance = null; 
} 

staną:

if (comInstance != null) 
{ 
    comInstance.FreeStuff(); 

    int count = Marshall.ReleaseComObject(comInstance); 
    if (count != 0) 
    { 
      Debug.Assert(false, "comInstance count = " + count); 
      Marshal.FinalReleaseComObject(comInstance); 
    } 

    comInstance = null; 
} 

Podczas sprawdzania wartości zwracanej ReleaseComObject() nie jest bezwzględnie konieczne, chciałbym sprawdzić go upewnij się, że rzeczy są inkrementowane/zmniejszane zgodnie z oczekiwaniami.

Reguła 2-kropka

Jeśli zdecydujesz się korzystać z tego, coś mieć świadomość, że niektóre kod może być konieczne do prawidłowego refactored zwolnić swoich obiektów COM. Jednym z nich jest w szczególności zasada 2 punktów. Każda linia używająca obiektów COM, które zawierają 2 kropki, wymaga szczególnej uwagi. Na przykład,

var name = myComObject.Address.Name; 

W tym stwierdzeniu możemy uzyskać referencję do obiektu Adres COM zwiększany jej licznika odwołań RCW, ale nie mamy okazji, aby zadzwonić ReleaseComObject. Lepszym sposobem na to byłoby rozbicie go (try..finally pominięte dla jasności):

var address = myComObject.Address; 
var name = address.Name; 
MyReleaseComObject(address); 

gdzie MyReleaseComObject to metoda narzędzie owijania czek policzyć i FinalReleaseComObject() z góry.

Powiązane problemy