CA2202 วิธีแก้ปัญหากรณีนี้


103

ใครช่วยบอกวิธีลบคำเตือน CA2202 ทั้งหมดออกจากโค้ดต่อไปนี้ได้ไหม

public static byte[] Encrypt(string data, byte[] key, byte[] iv)
{
    using(MemoryStream memoryStream = new MemoryStream())
    {
        using (DESCryptoServiceProvider cryptograph = new DESCryptoServiceProvider())
        {
            using (CryptoStream cryptoStream = new CryptoStream(memoryStream, cryptograph.CreateEncryptor(key, iv), CryptoStreamMode.Write))
            {
                using(StreamWriter streamWriter = new StreamWriter(cryptoStream))
                {
                    streamWriter.Write(data);
                }
            }
        }
        return memoryStream.ToArray();
    }
}

คำเตือน 7 CA2202: Microsoft.Usage: Object 'cryptoStream' สามารถกำจัดได้มากกว่า 1 ครั้งในวิธีการ 'CryptoServices.Encrypt (string, byte [], byte [])' เพื่อหลีกเลี่ยงการสร้าง System.ObjectDisposedException คุณไม่ควรเรียก Dispose มากกว่าหนึ่งครั้งบนวัตถุ: เส้น: 34

คำเตือน 8 CA2202: Microsoft.Usage: Object 'memoryStream' สามารถกำจัดได้มากกว่าหนึ่งครั้งในเมธอด 'CryptoServices.Encrypt (string, byte [], byte [])' เพื่อหลีกเลี่ยงการสร้าง System.ObjectDisposedException คุณไม่ควรเรียก Dispose มากกว่าหนึ่งครั้งบนออบเจ็กต์: Lines: 34, 37

คุณต้องใช้ Visual Studio Code Analysis เพื่อดูคำเตือนเหล่านี้ (ไม่ใช่คำเตือนของคอมไพเลอร์ c #)


1
รหัสนี้ไม่สร้างคำเตือนเหล่านี้
Julien Hoarau

1
ฉันได้รับคำเตือน 0 รายการสำหรับสิ่งนี้ (เตือนระดับ 4, VS2010) และสำหรับบางคนที่มีปัญหาในการ Googling ในพื้นที่นี้โปรดเพิ่มข้อความของคำเตือนด้วย
Henk Holterman

29
คำเตือนCAxxxxสร้างขึ้นโดยการวิเคราะห์โค้ดและ FxCop
dtb

คำเตือนนี้ใช้ไม่ได้กับโค้ดที่แสดง - คำเตือนสามารถระงับได้สำหรับสถานการณ์นี้ เมื่อคุณตรวจสอบรหัสของคุณและยอมรับการประเมินแล้วให้วางสิ่งนี้ไว้เหนือวิธีการของคุณ: " [SuppressMessage("Microsoft.Usage", "CA2202:Do not dispose objects multiple times", Justification="BrainSlugs83 said so.")]" - ตรวจสอบว่าคุณมีusing System.Diagnostics.CodeAnalysis;คำสั่ง "" ในบล็อกการใช้งานของคุณ
BrainSlugs83

คำตอบ:


-4

สิ่งนี้รวบรวมโดยไม่มีการเตือน:

    public static byte[] Encrypt(string data, byte[] key, byte[] iv)
    {
        MemoryStream memoryStream = null;
        DESCryptoServiceProvider cryptograph = null;
        CryptoStream cryptoStream = null;
        StreamWriter streamWriter = null;
        try
        {
            memoryStream = new MemoryStream();
            cryptograph = new DESCryptoServiceProvider();
            cryptoStream = new CryptoStream(memoryStream, cryptograph.CreateEncryptor(key, iv), CryptoStreamMode.Write);
            var result = memoryStream;              
            memoryStream = null;
            streamWriter = new StreamWriter(cryptoStream);
            cryptoStream = null;
            streamWriter.Write(data);
            return result.ToArray();
        }
        finally
        {
            if (memoryStream != null)
                memoryStream.Dispose();
            if (cryptograph != null)
                cryptograph.Dispose();
            if (cryptoStream != null)
                cryptoStream.Dispose();
            if (streamWriter != null)
                streamWriter.Dispose();
        }
    }

แก้ไขตามความคิดเห็น: ฉันเพิ่งตรวจสอบอีกครั้งว่ารหัสนี้ไม่สร้างคำเตือนในขณะที่รหัสเดิมทำ ในรหัสเดิมCryptoStream.Dispose()และMemoryStream().Dispose() จะถูกเรียกสองครั้ง (ซึ่งอาจเป็นปัญหาหรือไม่ก็ได้)

โค้ดที่แก้ไขจะทำงานดังนี้: การอ้างอิงถูกตั้งค่าnullเป็นทันทีที่ความรับผิดชอบในการกำจัดถูกโอนไปยังอ็อบเจ็กต์อื่น เช่นmemoryStreamถูกตั้งค่าเป็นnullหลังจากการเรียกตัวCryptoStreamสร้างสำเร็จ cryptoStreamถูกตั้งค่าเป็นnullหลังจากการเรียกตัวStreamWriterสร้างสำเร็จ หากไม่มีข้อยกเว้นเกิดขึ้นstreamWriterทิ้งในfinallyบล็อกและจะเปิดจำหน่ายและCryptoStreamMemoryStream


85
-1 มันเลวร้ายมากที่จะสร้างรหัสน่าเกลียดเพียงเพื่อให้สอดคล้องกับการเตือนว่าควรจะเก็บกด
Jordão

4
ฉันยอมรับว่าคุณไม่ควรฆ่าคุณรหัสสำหรับบางสิ่งที่อาจได้รับการแก้ไขในบางจุดในอนาคตเพียงแค่ระงับ
peteski

3
วิธีนี้แก้ปัญหาได้อย่างไร? CA2202 ยังคงรายงานเนื่องจาก memoryStream ยังสามารถกำจัดได้สองครั้งในบล็อกสุดท้าย
Chris Gessler

3
เนื่องจาก CryptoStream เรียก Dispose บน MemoryStream ภายในจึงสามารถเรียกได้สองครั้งซึ่งเป็นสาเหตุของคำเตือน ฉันลองวิธีแก้ปัญหาของคุณแล้ว แต่ยังคงได้รับคำเตือน
Chris Gessler

2
โอ้ jeez คุณพูดถูก - ฉันไม่ได้คาดหวังว่าจะมีตรรกะการล้างข้อมูลผสมกับ ... ตรรกะ - ตรรกะของคุณ ... - มันเป็นเรื่องแปลกและคลุมเครือ - มันฉลาดอย่างแน่นอน - แต่อีกครั้ง น่ากลัว - โปรดอย่าทำในรหัสการผลิต เพื่อความชัดเจน: คุณเข้าใจแล้วว่าไม่มีปัญหาการใช้งานจริงที่กำลังแก้ไขอยู่ถูกต้องหรือไม่? (ไม่เป็นไรที่จะทิ้งวัตถุเหล่านี้หลาย ๆ ครั้ง) - ฉันจะลบการโหวตลงถ้าทำได้ (ดังนั้นป้องกันฉันมันบอกว่าคุณต้องแก้ไขคำตอบ) - แต่ฉันจะทำโดยไม่เต็มใจ ... - และอย่างจริงจังอย่าทำแบบนี้
BrainSlugs83

142

คุณควรระงับคำเตือนในกรณีนี้ รหัสที่เกี่ยวข้องกับสิ่งที่ใช้แล้วทิ้งควรมีความสอดคล้องกันและคุณไม่จำเป็นต้องสนใจว่าคลาสอื่น ๆ จะเป็นเจ้าของสิ่งที่ใช้แล้วทิ้งที่คุณสร้างขึ้นและเรียกDisposeใช้ด้วย

[SuppressMessage("Microsoft.Usage", "CA2202:Do not dispose objects multiple times")]
public static byte[] Encrypt(string data, byte[] key, byte[] iv) {
  using (var memoryStream = new MemoryStream()) {
    using (var cryptograph = new DESCryptoServiceProvider())
    using (var cryptoStream = new CryptoStream(memoryStream, cryptograph.CreateEncryptor(key, iv), CryptoStreamMode.Write))
    using (var streamWriter = new StreamWriter(cryptoStream)) {
      streamWriter.Write(data);
    }
    return memoryStream.ToArray();
  }
}

UPDATE:ในIDisposableเอกสารประกอบการกำจัดคุณสามารถอ่านสิ่งนี้:

หากมีการเรียกเมธอด Dispose ของอ็อบเจ็กต์มากกว่าหนึ่งครั้งอ็อบเจ็กต์จะต้องละเว้นการเรียกทั้งหมดหลังจากอ็อบเจ็กต์แรก วัตถุต้องไม่เกิดข้อยกเว้นหากมีการเรียกเมธอด Dispose หลายครั้ง

อาจเป็นที่ถกเถียงกันอยู่ว่ากฎนี้มีอยู่เพื่อให้นักพัฒนาสามารถใช้ usingคำสั่งได้อย่างถูกต้องในชั้นวางของที่ใช้แล้วทิ้งเช่นที่ฉันได้แสดงไว้ข้างต้น (หรืออาจเป็นเพียงผลข้างเคียงที่ดี) ด้วยโทเค็นเดียวกันดังนั้น CA2202 จึงไม่มีจุดประสงค์ที่เป็นประโยชน์และควรระงับโครงการที่ชาญฉลาด ผู้กระทำผิดที่แท้จริงจะเป็นการดำเนินการที่ผิดพลาดDisposeและCA1065ควรดูแล (หากอยู่ภายใต้ความรับผิดชอบของคุณ)


14
ในความคิดของฉันนี่เป็นข้อผิดพลาดใน fxcop กฎนี้ไม่ถูกต้อง วิธีการกำจัดไม่ควรโยน ObjectDisposedException และหากเป็นเช่นนั้นคุณควรจัดการกับมันในเวลานั้นโดยการยื่นข้อบกพร่องเกี่ยวกับผู้เขียนโค้ดที่ใช้การกำจัดด้วยวิธีนี้
justin.m.chase

14
ฉันเห็นด้วยกับ @HansPassant ในหัวข้ออื่น: เครื่องมือกำลังทำงานและเตือนคุณเกี่ยวกับรายละเอียดการใช้งานคลาสที่ไม่คาดคิด โดยส่วนตัวแล้วฉันคิดว่าปัญหาที่แท้จริงคือการออกแบบ API เอง การมีคลาสที่ซ้อนกันเป็นค่าเริ่มต้นโดยสันนิษฐานว่าเป็นเรื่องปกติที่จะเป็นเจ้าของวัตถุอื่นที่สร้างขึ้นจากที่อื่นดูเหมือนจะเป็นเรื่องที่น่าสงสัย ฉันเห็นว่าจะมีประโยชน์ตรงไหนหากจะส่งคืนวัตถุที่เป็นผลลัพธ์ แต่การตั้งค่าเริ่มต้นให้กับสมมติฐานนั้นดูเหมือนจะต่อต้านได้ง่ายและละเมิดรูปแบบที่สามารถระบุได้ตามปกติ
BTJ

8
แต่ msdn ไม่แนะนำให้กด Supress ข้อความประเภทนี้ ดูได้ที่: msdn.microsoft.com/en-us/library/…
Adil Mammadov

2
ขอบคุณสำหรับลิงก์ @AdilMammadov ข้อมูลที่เป็นประโยชน์ แต่ไมโครซอฟท์มักไม่ถูกต้องเกี่ยวกับสิ่งเหล่านี้
Tim Abell

40

ถูกต้องเมธอด Dispose () บนสตรีมเหล่านี้จะถูกเรียกมากกว่าหนึ่งครั้ง คลาส StreamReader จะ 'เป็นเจ้าของ' ของ cryptoStream ดังนั้นการกำจัด streamWriter ก็จะกำจัด cryptoStream ไปด้วย ในทำนองเดียวกันคลาส CryptoStream จะเข้ามารับผิดชอบ memoryStream

นี่ไม่ใช่บั๊กที่แท้จริงคลาส. NET เหล่านี้มีความยืดหยุ่นต่อการเรียก Dispose () หลายรายการ แต่ถ้าคุณต้องการกำจัดคำเตือนคุณควรทิ้งคำสั่งใช้สำหรับวัตถุเหล่านี้ และทำให้ตัวเองเจ็บปวดเล็กน้อยเมื่อหาเหตุผลว่าจะเกิดอะไรขึ้นหากรหัสนั้นส่งข้อยกเว้น หรือปิดคำเตือนด้วยแอตทริบิวต์ หรือเพียงแค่เพิกเฉยต่อคำเตือนเนื่องจากเป็นเรื่องงี่เง่า


10
การต้องมีความรู้พิเศษเกี่ยวกับพฤติกรรมภายในของคลาส (เช่นการเป็นเจ้าของแบบใช้แล้วทิ้ง) นั้นมากเกินไปที่จะถามว่ามีใครต้องการออกแบบ API ที่ใช้ซ้ำได้หรือไม่ เลยคิดว่าusingงบน่าจะอยู่ คำเตือนเหล่านี้โง่จริงๆ
Jordão

4
@ Jordão - นั่นคือเครื่องมือสำหรับอะไร? เพื่อเตือนคุณเกี่ยวกับพฤติกรรมภายในที่คุณอาจไม่เคยรู้มาก่อน?
Hans Passant

8
ฉันเห็นด้วย. แต่ฉันยังคงไม่ทิ้งusingงบ รู้สึกผิดที่ต้องพึ่งพาวัตถุอื่นเพื่อกำจัดวัตถุที่ฉันสร้างขึ้น สำหรับโค้ดนี้ถือว่าใช้ได้ แต่มีการนำไปใช้งานStreamและใช้งานได้มากมายTextWriter(ไม่ใช่เฉพาะใน BCL) รหัสที่จะใช้ทั้งหมดควรสอดคล้องกัน
Jordão

3
ใช่เห็นด้วยกับJordão หากคุณต้องการให้โปรแกรมเมอร์ตระหนักถึงพฤติกรรมภายในของ API ให้พูดออกมาโดยตั้งชื่อฟังก์ชันของคุณเป็น DoSomethingAndDisposeStream (สตรีมสตรีมข้อมูล OtherData)
ZZZ

5
@HansPassant คุณสามารถชี้ให้เห็นได้ไหมว่าXmlDocument.Save()วิธีการนี้จะเรียกDisposeใช้พารามิเตอร์ที่ให้มา ฉันไม่เห็นสิ่งนี้ในเอกสารของSave(XmlWriter)(ที่ฉันพบข้อบกพร่องของ FxCop) หรือในSave()วิธีการนั้นเองหรือในเอกสารของXmlDocumentตัวมันเอง
Ian Boyd

9

เมื่อมีการกำจัดStreamWriterจะกำจัดสตรีมที่ห่อโดยอัตโนมัติ(ที่นี่: CryptoStream ) CryptoStreamจะกำจัดสตรีมที่ห่อโดยอัตโนมัติ(ที่นี่: MemoryStream )

ดังนั้นMemoryStreamของคุณจึงถูกกำจัดโดยCryptoStreamและคำสั่งใช้ และCryptoStreamของคุณถูกกำจัดโดยStreamWriterและคำสั่งใช้ภายนอก


หลังจากการทดลองบางอย่างดูเหมือนว่าจะไม่สามารถกำจัดคำเตือนได้อย่างสมบูรณ์ ในทางทฤษฎี MemoryStream ต้องถูกกำจัด แต่ในทางทฤษฎีแล้วคุณไม่สามารถเข้าถึงเมธอด ToArray ได้อีกต่อไป ในทางปฏิบัติ MemoryStream ไม่จำเป็นต้องถูกกำจัดดังนั้นฉันจะใช้วิธีแก้ปัญหานี้และระงับคำเตือน CA2000

var memoryStream = new MemoryStream();

using (var cryptograph = new DESCryptoServiceProvider())
using (var writer = new StreamWriter(new CryptoStream(memoryStream, ...)))
{
    writer.Write(data);
}

return memoryStream.ToArray();

"แล้วในทางทฤษฎีคุณไม่สามารถเข้าถึงเมธอด ToArray ได้อีกต่อไป" ดังนั้นให้เลื่อนreturnบรรทัดก่อนวงเล็บปีกกาปิดของusingขอบเขต
Ben Voigt

9

ฉันจะทำสิ่งนี้โดยใช้#pragma warning disable.

แนวทางของ. NET Framework แนะนำให้ใช้ IDisposable กำจัดในลักษณะที่สามารถเรียกใช้ได้หลายครั้ง จากคำอธิบาย MSDN ของ IDisposable.Dispose :

วัตถุต้องไม่เกิดข้อยกเว้นหากมีการเรียกเมธอด Dispose หลายครั้ง

ดังนั้นคำเตือนดูเหมือนจะแทบไม่มีความหมาย:

เพื่อหลีกเลี่ยงการสร้าง System.ObjectDisposedException คุณไม่ควรเรียก Dispose มากกว่าหนึ่งครั้งบนวัตถุ

ฉันเดาว่าอาจเป็นที่ถกเถียงกันอยู่ว่าคำเตือนอาจมีประโยชน์หากคุณใช้ออบเจ็กต์ IDisposable ที่ใช้งานไม่ดีซึ่งไม่เป็นไปตามแนวทางการใช้งานมาตรฐาน แต่เมื่อใช้คลาสจาก. NET Framework เหมือนที่คุณกำลังทำอยู่ฉันว่าปลอดภัยที่จะระงับคำเตือนโดยใช้ #pragma และ IMHO วิธีนี้ดีกว่าที่จะดำเนินการผ่านห่วงตามที่แนะนำในเอกสาร MSDN สำหรับคำเตือนนี้


4
CA2202 เป็นคำเตือนการวิเคราะห์โค้ดไม่ใช่คำเตือนของคอมไพเลอร์ #pragma warning disableสามารถใช้เพื่อระงับคำเตือนของคอมไพเลอร์เท่านั้น หากต้องการระงับคำเตือนการวิเคราะห์โค้ดคุณต้องใช้แอตทริบิวต์
Martin Liversage

2

ฉันประสบกับปัญหาที่คล้ายกันในรหัสของฉัน

ดูเหมือนว่าสิ่ง CA2202 ทั้งหมดถูกทริกเกอร์เนื่องจากMemoryStreamสามารถกำจัดได้หากมีข้อยกเว้นเกิดขึ้นในตัวสร้าง (CA2000)

สิ่งนี้สามารถแก้ไขได้ดังนี้:

 1 public static byte[] Encrypt(string data, byte[] key, byte[] iv)
 2 {
 3    MemoryStream memoryStream = GetMemoryStream();
 4    using (DESCryptoServiceProvider cryptograph = new DESCryptoServiceProvider())
 5    {
 6        CryptoStream cryptoStream = new CryptoStream(memoryStream, cryptograph.CreateEncryptor(key, iv), CryptoStreamMode.Write);
 7        using (StreamWriter streamWriter = new StreamWriter(cryptoStream))
 8        {
 9            streamWriter.Write(data);
10            return memoryStream.ToArray();
11        }
12    }
13 }
14
15 /// <summary>
16 /// Gets the memory stream.
17 /// </summary>
18 /// <returns>A new memory stream</returns>
19 private static MemoryStream GetMemoryStream()
20 {
21     MemoryStream stream;
22     MemoryStream tempStream = null;
23     try
24     {
25         tempStream = new MemoryStream();
26
27         stream = tempStream;
28         tempStream = null;
29     }
30     finally
31     {
32         if (tempStream != null)
33             tempStream.Dispose();
34     }
35     return stream;
36 }

สังเกตว่าเราต้องส่งคืนmemoryStreamภายในusingคำสั่งสุดท้าย(บรรทัดที่ 10) เนื่องจากcryptoStreamถูกกำจัดที่บรรทัด 11 (เนื่องจากใช้ในstreamWriter usingคำสั่ง) ซึ่งนำไปสู่memoryStreamการกำจัดที่บรรทัดที่ 11 ด้วย (เพราะmemoryStreamใช้ในการสร้างcryptoStream)

อย่างน้อยรหัสนี้ก็ใช้ได้สำหรับฉัน

แก้ไข:

มันอาจฟังดูตลกฉันค้นพบว่าถ้าคุณแทนที่GetMemoryStreamวิธีการด้วยรหัสต่อไปนี้

/// <summary>
/// Gets a memory stream.
/// </summary>
/// <returns>A new memory stream</returns>
private static MemoryStream GetMemoryStream()
{
    return new MemoryStream();
}

คุณจะได้รับผลลัพธ์เดียวกัน


1

cryptostream ขึ้นอยู่กับสตรีมหน่วยความจำ

สิ่งที่ดูเหมือนจะเกิดขึ้นคือเมื่อ crypostream ถูกกำจัด (เมื่อสิ้นสุดการใช้งาน) สตรีมหน่วยความจำก็จะถูกกำจัดไปด้วยเช่นกันสตรีมความจำจะถูกกำจัดอีกครั้ง


1

ฉันต้องการแก้ปัญหานี้ด้วยวิธีที่ถูกต้องนั่นคือโดยไม่ต้องระงับคำเตือนและทิ้งวัตถุที่ใช้แล้วทิ้งทั้งหมดอย่างถูกต้อง

ฉันดึงสตรีม 2 จาก 3 สตรีมออกมาเป็นฟิลด์และกำจัดมันด้วยDispose()วิธีการของคลาสของฉัน ใช่การติดตั้งIDisposableอินเทอร์เฟซอาจไม่จำเป็นต้องเป็นสิ่งที่คุณกำลังมองหา แต่วิธีการแก้ปัญหานั้นดูสะอาดตาเมื่อเทียบกับการdispose()โทรจากที่สุ่มทั้งหมดในโค้ด

public class SomeEncryption : IDisposable
    {
        private MemoryStream memoryStream;

        private CryptoStream cryptoStream;

        public static byte[] Encrypt(string data, byte[] key, byte[] iv)
        {
             // Do something
             this.memoryStream = new MemoryStream();
             this.cryptoStream = new CryptoStream(this.memoryStream, encryptor, CryptoStreamMode.Write);
             using (var streamWriter = new StreamWriter(this.cryptoStream))
             {
                 streamWriter.Write(plaintext);
             }
            return memoryStream.ToArray();
        }

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

       protected virtual void Dispose(bool disposing)
        {
            if (disposing)
            {
                if (this.memoryStream != null)
                {
                    this.memoryStream.Dispose();
                }

                if (this.cryptoStream != null)
                {
                    this.cryptoStream.Dispose();
                }
            }
        }
   }

0

นอกหัวข้อ แต่ฉันขอแนะนำให้คุณใช้เทคนิคการจัดรูปแบบอื่นสำหรับการจัดกลุ่มusings:

using (var memoryStream = new MemoryStream())
{
    using (var cryptograph = new DESCryptoServiceProvider())
    using (var encryptor = cryptograph.CreateEncryptor(key, iv))
    using (var cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write))
    using (var streamWriter = new StreamWriter(cryptoStream))
    {
        streamWriter.Write(data);
    }

    return memoryStream.ToArray();
}

ฉันยังสนับสนุนให้ใช้vars ที่นี่เพื่อหลีกเลี่ยงการใช้ชื่อคลาสที่ยาวมาก ๆ ซ้ำ ๆ

PS ขอบคุณที่ @ShellShock สำหรับการชี้ออกฉันไม่สามารถจัดฟันงดครั้งแรกusingที่มันจะทำให้memoryStreamในreturnคำสั่งออกจากขอบเขต


5
memoryStream. ToArray () จะไม่อยู่นอกขอบเขตหรือ?
Polyfun

ซึ่งเทียบเท่ากับโค้ดต้นฉบับอย่างแน่นอน ฉันเพิ่งใช้เครื่องหมายวงเล็บปีกกาเหมือนที่คุณสามารถทำได้กับifs (แม้ว่าฉันจะไม่แนะนำเทคนิคนี้สำหรับสิ่งอื่นนอกจากusings)
Dan Abramov

2
ในรหัสเดิม memoryStreamtoArray () อยู่ในขอบเขตของการใช้ครั้งแรก คุณได้รับมันนอกขอบเขต
Polyfun

ขอบคุณมากฉันเพิ่งรู้ว่าคุณหมายถึงreturnคำพูด จริง ฉันแก้ไขคำตอบเพื่อสะท้อนสิ่งนี้
Dan Abramov

โดยส่วนตัวแล้วฉันคิดว่าการusingไม่มีวงเล็บปีกกาทำให้โค้ดเปราะบางมากขึ้น (คิดว่าหลายปีของความแตกต่างและการผสาน) joelonsoftware.com/2005/05/11/making-wrong-code-look-wrong & imperialviolet.org/2014/02/22/applebug.html
Tim Abell

0

หลีกเลี่ยงการใช้งานทั้งหมดและใช้ Dispose-Call ที่ซ้อนกัน!

    public static byte[] Encrypt(string data, byte[] key, byte[] iv)
    {
        MemoryStream memoryStream = null;
        DESCryptoServiceProvider cryptograph = null;
        CryptoStream cryptoStream = null;
        StreamWriter streamWriter = null;

        try
        {
            memoryStream = new MemoryStream();
            cryptograph = new DESCryptoServiceProvider();
            cryptoStream = new CryptoStream(memoryStream, cryptograph.CreateEncryptor(key, iv), CryptoStreamMode.Write);
            streamWriter = new StreamWriter(cryptoStream);

            streamWriter.Write(data);
            return memoryStream.ToArray();
        }
        finally 
        {
            if(streamWriter != null)
                streamWriter.Dispose();
            else if(cryptoStream != null)
                cryptoStream.Dispose();
            else if(memoryStream != null)
                memoryStream.Dispose();

            if (cryptograph != null)
                cryptograph.Dispose();
        }
    }

1
โปรดอธิบายเหตุผลที่คุณควรหลีกเลี่ยงusingในกรณีนี้
StuperUser

1
คุณสามารถให้คำสั่งใช้อยู่ตรงกลางได้ แต่คุณต้องแก้ไขคำสั่งอื่น ๆ เพื่อให้ได้โซลูชันที่สามารถอัพเกรดได้อย่างสอดคล้องกันและในทุกทิศทางฉันตัดสินใจที่จะลบการใช้งานทั้งหมด!
Harry Saltzman

0

ฉันแค่ต้องการแกะโค้ดเพื่อให้เราสามารถเห็นการเรียกหลายครั้งDisposeบนวัตถุ:

memoryStream = new MemoryStream()
cryptograph = new DESCryptoServiceProvider()
cryptoStream = new CryptoStream()
streamWriter = new StreamWriter()

memoryStream.Dispose(); //implicitly owned by cryptoStream
cryptoStream.Dispose(); //implicitly owned by streamWriter
streamWriter.Dispose(); //through a using

cryptoStream.Dispose(); //INVALID: second dispose through using
cryptograph.Dispose(); //through a using
memorySTream.Dipose(); //INVALID: second dispose through a using

return memoryStream.ToArray(); //INVALID: accessing disposed memoryStream

ในขณะที่คลาส. NET ส่วนใหญ่ (หวังว่า) จะมีความยืดหยุ่นต่อความผิดพลาดของการโทรหลายครั้ง.Disposeแต่คลาสทั้งหมดไม่สามารถป้องกันการใช้โปรแกรมเมอร์ในทางที่ผิดได้

FX Cop รู้เรื่องนี้และเตือนคุณ

คุณมีทางเลือกไม่กี่ทาง

  • โทรเพียงDisposeครั้งเดียวในวัตถุใด ๆ อย่าใช้using
  • โทรต่อไปทิ้งสองครั้งและหวังว่ารหัสจะไม่ผิดพลาด
  • ระงับคำเตือน

-1

ฉันใช้รหัสประเภทนี้ที่ใช้ไบต์ [] และส่งคืนไบต์ [] โดยไม่ต้องใช้สตรีม

public static byte[] Encrypt(byte[] data, byte[] key, byte[] iv)
{
  DES des = new DES();
  des.BlockSize = 128;
  des.Mode = CipherMode.CBC;
  des.Padding = PaddingMode.Zeros;
  des.IV = IV
  des.Key = key
  ICryptoTransform encryptor = des.CreateEncryptor();

  //and finaly operations on bytes[] insted of streams
  return encryptor.TransformFinalBlock(plaintextarray,0,plaintextarray.Length);
}

วิธีนี้สิ่งที่คุณต้องทำคือการแปลงจากสตริงเป็นไบต์ [] โดยใช้การเข้ารหัส

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.