2009-07-28 25 views
5

Lớp học của tôi chứa một đối tượng từ Interop và gọi một phương thức trên đó làm cho nó phân bổ nội dung. Nó cũng đưa ra một phương pháp để giải phóng nội dung đó, vì vậy tôi hy vọng tôi nên gọi điều này trong Dispose():Làm cách nào để triển khai mẫu vứt bỏ trong C# khi gói một đối tượng COM Interop?

class MyClass : IDisposable 
{ 
    private DllName.ComClassName comInstance; 

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

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

Bây giờ, tôi nên mở rộng tất cả để theo mẫu Vứt bỏ. Tôi không có nguồn lực dùng một lần hoặc không được quản lý khác để phát hành, vì vậy giả sử comInstance được quản lý (không phải là những gì Interop không, kết thúc tốt đẹp không được quản lý vào quản lý?), Tôi nghĩ rằng disolves mẫu để:

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

nào rò rỉ, trừ khi tôi rõ ràng gọi Dispose() trên các trường hợp của MyClass, điều này sẽ làm cho mẫu Dispose bị thiếu sót? Vì vậy, điều đó có nghĩa comInstance phải được quản lý, và các mô hình disolves tới:

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

~MyClass() 
{ 
    DisposeComInstance(); 
} 

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

EDIT:

  1. Để tránh làm lộn xộn lớp học của tôi với mô hình đầy đủ, tôi có thể chỉ niêm phong lớp học của tôi?
  2. Làm cách nào để biết ComClassName (và nói chung là bất kỳ lớp học nào) không được quản lý?

Trả lời

2

Dường như bạn gần như đã bị đóng đinh. Tôi sẽ quay trở lại mẫu mà bạn có một Disposing ảo được bảo vệ để nhận một tham số boolean cho biết các mục được quản lý có nên được xử lý hay không. Bằng cách đó, ai đó đến sau bạn sẽ tiếp tục đến 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

Siêu nhận xét: hãy chắc chắn rằng Dispose (bool) của bạn có thể chịu đựng được gọi nhiều lần liên tiếp. Cũng cố gắng để giảm thiểu khả năng của một ngoại lệ được ném (không phải lúc nào cũng có thể). – user7116

+1

Nhận xét meta meta - cũng như điều đó, điều quan trọng là bạn không bao giờ có được khóa hoặc sử dụng khóa trong khi dọn dẹp không được quản lý của bạn. – womp

3

Cuối cùng bạn muốn loại mẫu:

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

Đối với một bài viết tuyệt vời về việc tại sao, hãy kiểm tra Implementing IDisposable and the Dispose pattern properly.

+0

-1, Bạn cần di chuyển các đối tượng không được quản lý ra bên ngoài khối if (xử lý). Không được quản lý nên luôn luôn được làm sạch. – user7116

+0

Gah! Bạn đã đúng, tôi đã sao chép nó từ một số mã của tôi đang sử dụng các thành phần có mã không được quản lý nhưng thực hiện vứt bỏ và chỉnh sửa trong ví dụ của OP ... dán cho phần bị mất. Đã sửa. – womp

+0

+1, được triển khai chính xác ;-D – user7116

3

Trước hết, tôi đồng ý với những đề nghị có một finalizer như một bản sao lưu , nhưng cố gắng tránh nó bao giờ được gọi bằng cách gọi một cách rõ ràng myClass.Dispose hoặc thông qua 'using'.

ví dụ:

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

hoặc

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

Marshall.ReleaseComObject

Nếu bạn đang sử dụng rất nhiều các đối tượng COM, tôi cũng đề nghị bạn sử dụng Mashall.ReleaseComObject (comObj) để rõ ràng dọn dẹp tài liệu tham khảo để RCW.

Vì vậy, các mã như thế này gợi ý khác:

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

sẽ trở thành:

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

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

    comInstance = null; 
} 

Trong khi kiểm tra giá trị trở lại của ReleaseComObject() là không thực sự cần thiết, tôi muốn kiểm tra xem nó để đảm bảo mọi thứ đang được tăng/giảm dần như mong đợi.

2-dot quy tắc

Nếu bạn quyết định sử dụng này, một cái gì đó phải nhận thức được là một số mã có thể cần phải được refactored để phát hành đúng đối tượng COM của bạn. Một đặc biệt là cái mà tôi gọi là quy tắc 2 dấu chấm. Bất kỳ dòng nào sử dụng các đối tượng COM có chứa 2 dấu chấm đều cần chú ý kỹ. Ví dụ,

var name = myComObject.Address.Name; 

Trong tuyên bố này, chúng ta có được một tham chiếu đến đối tượng Địa chỉ COM incrementing đếm RCW tham chiếu của nó, nhưng chúng tôi không có cơ hội để gọi ReleaseComObject. Một cách tốt hơn để làm điều đó sẽ là để phá vỡ nó xuống (try..finally bỏ qua cho rõ ràng):

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

nơi MyReleaseComObject là một phương pháp hữu ích gói kiểm đếm của tôi và FinalReleaseComObject() từ trên cao.

Các vấn đề liên quan