CA2202, comment résoudre cette affaire

quelqu'un peut-il me dire comment supprimer tous les Avertissements CA2202 du code suivant?

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

avertissement 7 CA2202: Microsoft.Utilisation: L'objet 'cryptoStream' peut être disposé plus d'une fois dans la méthode 'CryptoServices.Chiffrer(string, byte[], byte[])'. Pour éviter de générer un Système.ObjectDisposedException vous ne devez pas appeler Dispose plus d'une fois sur un objet.: Lignes: 34

avertissement 8 CA2202 : Microsoft.Utilisation: L'objet 'memoryStream' peut être éliminé plus d'une fois dans la méthode 'CryptoServices.Chiffrer(string, byte[], byte[])'. Pour éviter de générer un Système.ObjectDisposedException vous ne devez pas appeler Dispose plus d'une fois sur un objet.: Lines: 34, 37

vous avez besoin D'une analyse visuelle du Code Studio pour voir ces avertissements (ce ne sont pas des avertissements C# compiler).

93
demandé sur Ben Smith 2010-09-30 18:41:11

12 réponses

Cette compile sans warning:

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

Modifier en réponse aux commentaires: Je viens de vérifier à nouveau que ce code ne génère pas l'avertissement, alors que l'original le fait. Dans le code original, CryptoStream.Dispose() et MemoryStream().Dispose( ) sont en fait appelés deux fois (ce qui peut être ou non un problème).

le code modifié fonctionne comme suit: les références sont définies à null , dès que la responsabilité pour disposer est transféré à un autre objet. Par exemple: memoryStream est défini à null après l'appel à CryptoStream constructeur réussi. cryptoStream est défini à null , après l'appel à StreamWriter constructeur réussi. Si aucune exception ne se produit, streamWriter est éliminé dans le bloc finally et sera à son tour éliminé CryptoStream et MemoryStream .

-1
répondu Henrik 2012-06-25 08:01:42

Vous devez supprimer les avertissements dans ce cas. Le Code qui traite des biens jetables devrait être cohérent, et vous ne devriez pas avoir à vous soucier que d'autres classes prennent la propriété des biens jetables que vous avez créés et aussi appeler Dispose sur eux.

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

mise à JOUR: Dans le IDisposable.Disposez de la documentation vous pouvez lire ceci:

si la méthode D'élimination d'un objet est appelée plus d'une fois, l'objet doit ignorer tous les appels après la première. L'objet ne doit pas lancer d'exception si sa méthode Dispose est appelée plusieurs fois.

on peut faire valoir que cette règle existe pour que les développeurs puissent employer la déclaration using sainement dans une cascade de jetables, comme je l'ai montré ci-dessus (ou peut-être que c'est juste un bon effet secondaire). Dans le même ordre d'idées, CA2202 ne sert à rien et devrait être supprimé du point de vue du projet. Le véritable coupable serait une mauvaise mise en œuvre de Dispose , et CA1065 devrait prendre soin de cela (si elle est sous votre responsabilité).

128
répondu Jordão 2018-07-31 11:52:31

Eh bien, c'est EXACT, la méthode Dispose() sur ces flux sera appelée plus d'une fois. La classe StreamReader va prendre "ownership" de la cryptoStream donc se débarrasser streamWriter va également disposer de cryptoStream. De même, la classe CryptoStream prend en charge le memoryStream.

ce ne sont pas exactement de vrais bogues, ces classes .NET sont résistantes aux appels multiples De Dispose (). Mais si vous voulez vous débarrasser de l'avertissement alors vous devez déposer la déclaration d'utilisation pour ces objets. Et faites-vous un peu de peine en raisonnant ce qui se passera si le code jette une exception. Ou fermer le message d'avertissement avec un attribut. Ou ignorez l'avertissement car il est stupide.

41
répondu Hans Passant 2018-01-02 08:20:43

Lorsqu'un StreamWriter est éliminé, il dispose automatiquement du Stream (ici: le CryptoStream ). CryptoStream dispose aussi automatiquement le Stream (ici: le MemoryStream ).

donc votre MemoryStream est éliminé par le CryptoStream et le en utilisant . Et votre CryptoStream est éliminé par le StreamWriter et l'extérieur en utilisant déclaration.


après quelques expérimentations, il semble impossible de se débarrasser complètement des avertissements. Theorectically, MemoryStream doit être éliminée, mais alors vous, théoriquement, ne pouvait pas accéder à sa méthode ToArray plus. En pratique, un MemoryStream n'a pas besoin d'être éliminé, donc j'utiliserais cette solution et supprimerais L'avertissement CA2000.

var memoryStream = new MemoryStream();

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

return memoryStream.ToArray();
8
répondu dtb 2010-09-30 15:33:13

je le ferais en utilisant #pragma warning disable .

les lignes directrices du .net Framework recommandent de mettre en œuvre IDisposable.Disposer de manière à ce qu'il puisse être appelé plusieurs fois. De la MSDN description de IDisposable.Disposer :

L'objet ne doit pas jeter une exception si sa méthode Dispose est appelée plusieurs fois

par conséquent, l'avertissement semble être presque sans signification:

pour éviter de générer un système.ObjectDisposedException vous ne devriez pas appeler dispose de plus d'une heure sur un objet

je suppose que l'on pourrait faire valoir que l'avertissement peut être utile si vous utilisez un objet IDisposable mal mis en œuvre qui ne respecte pas les lignes directrices de mise en œuvre standard. Mais en utilisant les classes du Framework .NET comme vous le faites, je dirais qu'il est sûr de supprimer l'avertissement en utilisant un # pragma. Et IMHO ceci est préférable à passer par des cerceaux comme suggéré dans la documentation MSDN pour cet avertissement .

8
répondu Joe 2010-09-30 15:41:10

le cryptostream est basé sur le memorystream.

ce qui semble se produire, c'est que lorsque la crypostream est éliminée (à la fin de l'utilisation), le memorystream est également éliminé, puis le memorystream est éliminé à nouveau.

1
répondu Shiraz Bhaiji 2010-09-30 15:00:30

hors-sujet mais je vous suggérerais d'utiliser une technique de formatage différente pour grouper using s:

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

je recommande également l'utilisation de var s ici pour éviter les répétitions de noms de classe vraiment long.

merci à @ShellShock de souligner que je ne peux pas omettre les bretelles pour le premier using comme il ferait memoryStream dans return déclaration hors de la portée.

1
répondu Dan Abramov 2010-09-30 15:32:30

j'ai été confronté à des problèmes similaires dans mon code.

ressemble à L'ensemble CA2202 chose est déclenché parce que MemoryStream peut être éliminé si l'exception se produit dans le constructeur (CA2000).

Cela pourrait être résolu comme ceci:

 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 }

notez que nous devons retourner le memoryStream à l'intérieur du dernier énoncé using (ligne 10) parce que le cryptoStream est éliminé à la ligne 11 (parce qu'il est utilisé dans streamWriter using ), ce qui amène memoryStream à être aussi éliminé à la ligne 11 (parce que memoryStream est utilisé pour créer le cryptoStream ).

au moins ce code a fonctionné pour moi.

EDIT:

aussi drôle que cela puisse paraître, j'ai découvert que si vous remplacez la méthode GetMemoryStream par le code suivant,

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

, vous obtenez le même résultat.

1
répondu Jimi 2011-11-20 11:46:22

j'ai voulu résoudre cela de la bonne manière - c'est-à-dire sans supprimer les avertissements et en disposant correctement tous les objets jetables.

j'ai retiré 2 des 3 ruisseaux comme champs et les ai disposés dans la méthode Dispose() de ma classe. Oui, la mise en œuvre de l'interface IDisposable pourrait ne pas être nécessairement ce que vous recherchez, mais la solution semble assez propre par rapport aux appels dispose() de tous les endroits aléatoires dans le code.

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();
                }
            }
        }
   }
1
répondu divyanshm 2014-07-11 07:40:19

j'ai utilisé ce genre de code qui prend byte [] et byte de retour [] sans utiliser les flux

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

de cette façon, tout ce que vous avez à faire est la conversion de chaîne en octet[] en utilisant des encodages.

0
répondu Luka Rahne 2010-09-30 20:12:45

évitez toutes les utilisations et utilisez les appels D'élimination emboîtés!

    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();
        }
    }
0
répondu Harry Saltzman 2012-10-22 10:55:54

je voulais juste déballer le code pour qu'on puisse voir plusieurs appels à Dispose sur les objets:

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

alors que la plupart des classes .NET sont (espérons) résilientes contre l'erreur des appels multiples à .Dispose , pas toutes les classes sont aussi défensives contre l'utilisation abusive de programmeur.

FX Cop sait cela, et vous avertit.

Vous avez un peu de choix;

  • n'appelez Dispose qu'une fois sur n'importe quel objet; n'utilisez pas using
  • continuez à appeler disposez deux fois, et espérez que le code ne s'écroule pas
  • supprimer l'avertissement
0
répondu Ian Boyd 2015-06-04 15:39:24