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).
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
.
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é).
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.
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();
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 .
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.
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.
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.
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();
}
}
}
}
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.
é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();
}
}
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 pasusing
- continuez à appeler disposez deux fois, et espérez que le code ne s'écroule pas
- supprimer l'avertissement