2011-11-18 9 views
11

Tôi có đoạn code sauthực hiện IDisposable đúng cho mã này

public static byte[] Compress(byte[] CompressMe) 
{ 
    using (MemoryStream ms = new MemoryStream()) 
    { 
     using (GZipStream gz = new GZipStream(ms, CompressionMode.Compress,true)) 
     { 
      gz.Write(CompressMe, 0, CompressMe.Length); 
      ms.Position = 0; 
      byte[] Result = new byte[ms.Length]; 
      ms.Read(Result, 0, (int)ms.Length); 
      return Result; 
     } 
    } 
} 

này hoạt động tốt, nhưng khi tôi chạy phân tích mã trên nó, nó đi kèm với thông báo sau

CA2202 : Microsoft.Usage : Object 'ms' can be disposed more than once in 
method 'Compression.Compress(byte[])'. To avoid generating a 
System.ObjectDisposedException you should not call Dispose more than one 
time on an object. 

As far như tôi đang quan tâm, khi GZipStream được Disposed, nó rời khỏi Stream (ms) nằm bên dưới, do tham số cuối cùng của hàm tạo (leftOpen = true).

Nếu tôi thay đổi mã của tôi hơi .. loại bỏ các 'sử dụng' khối xung quanh MemoryStream và thay đổi thông số 'leaveOpen' false ..

public static byte[] Compress(byte[] CompressMe) 
{ 
    MemoryStream ms = new MemoryStream(); 
    using (GZipStream gz = new GZipStream(ms, CompressionMode.Compress, false)) 
    { 
     gz.Write(CompressMe, 0, CompressMe.Length); 
     ms.Position = 0; 
     byte[] Result = new byte[ms.Length]; 
     ms.Read(Result, 0, (int)ms.Length); 
     return Result; 
    } 
} 

này sau đó đi lên với ..

CA2000 : Microsoft.Reliability : In method 'Compression.Compress(byte[])', 
object 'ms' is not disposed along all exception paths. Call 
System.IDisposable.Dispose on object 'ms' before all references to 
it are out of scope. 

Tôi không thể thắng .. (trừ khi tôi thiếu điều gì đó hiển nhiên) Tôi đã thử nhiều thứ khác nhau, như đặt thử/cuối cùng xung quanh khối, và Xử lý MemoryStream trong đó, nhưng có thể nói rằng tôi 'm xử lý nó hai lần, hoặc không phải ở tất cả !!

+5

Thật lạ lùng. Từ [msdn docs] (http://msdn.microsoft.com/en-us/library/b1yfkh5e.aspx): [...] Dispose method [...] nên được gọi nhiều lần mà không cần ném một ngoại lệ (ObjectDisposedException)). – oleksii

+0

CA2000 là một pita khổng lồ. Theo kinh nghiệm của tôi, nó tạo ra nhiều mặt tích cực hơn so với cảnh báo genuiune. Tất cả những gì [tiếng sói khóc] (http://en.wikipedia.org/wiki/The_Boy_Who_Cried_Wolf) bây giờ có nghĩa là tôi có xu hướng chỉ bỏ qua/ngăn chặn CA2000 bất cứ khi nào nó mọc lên. – LukeH

+1

Bạn không thể thắng. Hãy sửa lỗi trong mã của bạn, gz cần Flush() hoặc đóng để tạo tất cả các byte. –

Trả lời

1

đó đôi khi là vấn đề với chạy CodeAnalysis, bạn đôi khi bạn chỉ đơn giản là không thể giành chiến thắng và bạn phải chọn cái ác ít ™.

Trong trường hợp này, tôi tin rằng việc triển khai chính xác là ví dụ thứ hai. Tại sao?Theo .NET Reflector, việc thực hiện GZipStream.Dispose() sẽ định đoạt của các MemoryStream cho bạn như GZipStreamsở hữu các MemoryStream.

phần có liên quan của GZipStream lớp dưới đây:

public GZipStream(Stream stream, CompressionMode mode, bool leaveOpen) 
{ 
    this.deflateStream = new DeflateStream(stream, mode, leaveOpen, true); 
} 

protected override void Dispose(bool disposing) 
{ 
    try 
    { 
     if (disposing && (this.deflateStream != null)) 
     { 
      this.deflateStream.Close(); 
     } 
     this.deflateStream = null; 
    } 
    finally 
    { 
     base.Dispose(disposing); 
    } 
} 

Như bạn sẽ không muốn vô hiệu hóa các quy tắc hoàn toàn, bạn có thể ngăn chặn đối với phương pháp này chỉ sử dụng bằng cách sử dụng thuộc tính CodeAnalysis.SupressMessage.

[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability ", "CA2000:?", Justification = "MemoryStream will be disposed by the GZipStream.")] 

Lưu ý: Bạn sẽ phải điền vào tên quy tắc đầy đủ (ví dụ: CA2000:?) vì tôi không biết nó là cái gì từ thông báo lỗi bạn đăng.

HTH,

EDIT:

@CodeInChaos:

Nhìn sâu hơn vào việc thực hiện DeflateStream.Dispose Tôi tin rằng nó vẫn sẽ định đoạt của MemoryStream cho bạn không phụ thuộc vào tùy chọn leaveOpen như nó gọi là base.Dispose().

EDIT Bỏ qua ở trên khoảng DeflateStream.Dispose. Tôi đã nhìn vào việc thực hiện sai trong Reflector. Xem nhận xét để biết chi tiết.

+0

Dường như nó đang chuyển tiếp công việc xử lý luồng bộ nhớ tới 'DeflateStream', có thể sẽ chỉ thực hiện nếu tham số' leaveOpen' là sai. – CodesInChaos

+0

@CodeInChaos: Cập nhật câu trả lời của tôi với nhiều chi tiết hơn. Tôi tin rằng tôi đã giải thích mã một cách chính xác. – Dennis

+0

Nó đặt trường '_stream' thành' null' trước khi gọi 'base.Dispose'. Nhưng trong trường hợp đó, phương pháp bạn đăng tải sẽ bị ném ... – CodesInChaos

3

Từ this page in the MSDN

Stream stream = null; 

try 
{ 
    stream = new FileStream("file.txt", FileMode.OpenOrCreate); 
    using (StreamWriter writer = new StreamWriter(stream)) 
    { 
     stream = null; 
     // Use the writer object... 
    } 
} 
finally 
{ 
    if(stream != null) 
     stream.Dispose(); 
} 

Đây là thử ... cuối cùng đó là mất tích từ giải pháp của bạn gây ra thông điệp thứ hai.

Nếu đây:

GZipStream gz = new GZipStream(ms, CompressionMode.Compress, false) 

thất bại dòng sẽ không được xử lý.

+0

Tôi đã thực sự thử ví dụ từ trang MSDN, nhưng điều đó nảy sinh cùng một lỗi phân tích mã. –

+0

cũng vậy, nếu các GZip contructor thất bại, nó vẫn nên thả ra khỏi 'sử dụng' khối chính xác .. đó là ý tưởng phải không? –

+0

Điều đó rất lạ ... khi tôi phân tích mã trong câu trả lời của tôi, nó không mang lại bất kỳ lỗi/cảnh báo nào –

0

Bạn phải đi học cũ:

public static byte[] Compress(byte[] CompressMe) 
{ 
    MemoryStream ms = null; 
    GZipStream gz = null; 
    try 
    { 
     ms = new MemoryStream(); 
     gz = new GZipStream(ms, CompressionMode.Compress, true); 
     gz.Write(CompressMe, 0, CompressMe.Length); 
     gz.Flush(); 
     return ms.ToArray(); 
    } 
    finally 
    { 
     if (gz != null) 
     { 
      gz.Dispose(); 
     } 
     else if (ms != null) 
     { 
      ms.Dispose(); 
     } 
    } 
} 

Trông khủng khiếp tôi biết, nhưng là cách duy nhất để xoa dịu các cảnh báo.

Cá nhân, tôi không thích viết mã như thế này, do đó, chỉ cần loại bỏ cảnh báo đa mục đích (nếu có) trên cơ sở là Vứt bỏ cuộc gọi phải là không đáng tin cậy (và trong trường hợp này).

+0

vâng, tôi có xu hướng đồng ý, tôi chắc chắn rằng tôi có thể giải quyết vấn đề này, có vẻ như việc sử dụng lệnh {} những thứ như thế này và đơn giản hóa mã. –

+0

Là khối sử dụng thậm chí cần thiết cho rằng sử dụng chỉ đơn giản là đường cú pháp để thử/cuối cùng? Không thể xử lý tất cả xảy ra trong khối cuối cùng? –

6

Ngoài vấn đề xử lý, mã của bạn cũng bị hỏng. Bạn nên đóng luồng zip trước khi đọc lại dữ liệu.

Cũng đã có phương thức ToArray() trên MemoryStream, không cần phải tự mình triển khai.

using (MemoryStream ms = new MemoryStream()) 
{ 
    using (GZipStream gz = new GZipStream(ms, CompressionMode.Compress,true)) 
    { 
     gz.Write(CompressMe, 0, CompressMe.Length); 
    } 
    return ms.ToArray(); 
} 

Tôi chỉ đơn giản là ngăn chặn cảnh báo, vì đó là xác thực sai. Mã phân tích là có để phục vụ bạn, không phải là cách khác tròn.

+0

+1. Tôi cũng tin rằng lỗi là * dương tính giả * và sau khi phân tích nó chọn triển khai chính xác nhất và ngăn chặn cảnh báo/quy tắc. – Dennis

+0

+1 Đặc biệt là vì đây là những công cụ được cho là phù hợp với chúng tôi, không phải chúng tôi làm việc cho họ. –

+0

cảm ơn vì đã chỉ ra phương thức ToArray(). –

2

Trong thực tế, việc gọi điện hai lần trên luồng bộ nhớ sẽ không gây ra bất kỳ sự cố nào, sẽ dễ dàng mã hóa điều này bên trong lớp MemoryStream và thử nghiệm mà Microsoft có vẻ. Do đó, nếu bạn không muốn ngăn chặn quy tắc, một cách khác là tách phương thức của bạn thành hai:

public static byte[] Compress(byte[] CompressMe) 
    { 
     using (MemoryStream ms = new MemoryStream()) 
     { 
      return Compress(CompressMe, ms); 
     } 
    } 

    public static byte[] Compress(byte[] CompressMe, MemoryStream ms) 
    { 
     using (GZipStream gz = new GZipStream(ms, CompressionMode.Compress, true)) 
     { 
      gz.Write(CompressMe, 0, CompressMe.Length); 
      ms.Position = 0; 
      byte[] Result = new byte[ms.Length]; 
      ms.Read(Result, 0, (int)ms.Length); 
      return Result; 
     } 
    }