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.