簡體   English   中英

在包裝Interop COM對象時,如何在c#中實現dispose模式?

[英]How do I implement the dispose pattern in c# when wrapping an Interop COM Object?

我的類包含來自Interop的對象,並在其上調用一個方法,使其分配內容。 它還公開了一個釋放這些東西的方法,所以我希望我應該在Dispose()中調用它:

class MyClass : IDisposable
{
    private DllName.ComClassName comInstance;

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

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

現在,我應該擴展所有這些以遵循Dispose模式。 我沒有其他一次性或非托管資源可以發布,所以假設comInstance是管理的(不是Interop所做的,將非托管包裝到托管?),我認為該模式解決了:

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

哪個泄漏除非我在MyClass的實例上顯式調用Dispose(),這會使Dispose模式有缺陷? 那么這是否意味着comInstance必須是非托管的,並且模式解析為:

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

~MyClass()
{
    DisposeComInstance();
}

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

編輯:

  1. 為了避免使用完整的模式使我的班級混亂,我可以封閉我的班級嗎?
  2. 我怎么知道ComClassName(通常是任何類)是不受管理的?

最終你需要這種模式:

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

有關原因的精彩文章,請查看正確實現IDisposable和Dispose模式。

首先,我同意那些建議將終結器作為備份的建議,但是盡量避免通過顯式調用myClass.Dispose或通過'using'來調用它。

例如

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

要么

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

Marshall.ReleaseComObject

如果你使用了很多COM對象,我還建議你使用Mashall.ReleaseComObject(comObj)來顯式清理對RCW的引用。

所以,這樣的代碼建議在別處:

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

會成為:

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

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

    comInstance = null;
}

雖然檢查ReleaseComObject()的返回值並不是絕對必要的,但我喜歡檢查它以確保事物按預期遞增/遞減。

2點規則

如果您決定使用它,需要注意的是,某些代碼可能需要重構才能正確釋放COM對象。 特別是我稱之為2點規則。 使用包含2個點的COM對象的任何行都需要密切關注。 例如,

var name = myComObject.Address.Name;

在這個語句中,我們得到一個對地址COM對象的引用,增加了它的RCW引用計數,但我們沒有機會調用ReleaseComObject。 更好的方法是將其分解(嘗試...為清晰起見省略):

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

其中MyReleaseComObject是一個實用程序方法,從上面包裝我的計數檢查和FinalReleaseComObject()。

看起來你幾乎把它釘了起來。 我會回到你有一個受保護的虛擬Disposing的模式,它采用一個布爾參數來指示是否應該處理被管理的項目。 這樣,有人會跟你一起繼續正確地實現IDisposable

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
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM