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.Usage: l'objet 'cryptoStream' peut être supprimé plusieurs fois dans la méthode 'CryptoServices.Encrypt (chaîne, octet [], octet [])'. Pour éviter de générer une System.ObjectDisposedException, vous ne devez pas appeler Dispose plus d'une fois sur un objet.: Lines: 34
Avertissement 8 CA2202: Microsoft.Usage: l'objet 'memoryStream' peut être supprimé plusieurs fois dans la méthode 'CryptoServices.Encrypt (chaîne, octet [], octet [])'. Pour éviter de générer une System.ObjectDisposedException, vous ne devez pas appeler Dispose plus d'une fois sur un objet .: Lignes: 34, 37
Vous avez besoin de Visual Studio Code Analysis pour voir ces avertissements (ce ne sont pas des avertissements du compilateur c #).
Vous devez supprimer les avertissements dans ce cas. Le code qui traite des objets jetables doit être cohérent, et vous ne devriez pas avoir à vous soucier que d'autres classes s'approprient les objets jetables que vous avez créés et appellent également Dispose
dessus.
[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 la documentation IDisposable.Dispose vous pouvez lire ceci:
Si la méthode Dispose d'un objet est appelée plusieurs fois, l'objet doit ignorer tous les appels après le premier. L'objet ne doit pas lever 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 utiliser la déclaration using
en toute sécurité dans une cascade de produits jetables, comme je l'ai montré ci-dessus (ou peut-être que c'est juste un effet secondaire agréable). De la même façon, CA2202 ne sert à rien et doit être supprimé par projet. Le vrai coupable serait une implémentation défectueuse de Dispose
, et CA1065 devrait s'en occuper (si c'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 s'appropriera le cryptoStream, donc l'élimination de streamWriter supprimera également le cryptoStream. De même, la classe CryptoStream prend la responsabilité du memoryStream.
Ce ne sont pas exactement de vrais bugs, ces classes .NET sont résistantes à plusieurs appels Dispose (). Mais si vous souhaitez vous débarrasser de l'avertissement, vous devez supprimer l'instruction using pour ces objets. Et faites-vous un peu mal en raisonnant ce qui se passera si le code lève une exception. Ou fermez l'avertissement avec un attribut. Ou ignorez simplement l'avertissement, car c'est idiot.
Lorsqu'un StreamWriter est supprimé, il supprime automatiquement le Stream encapsulé (ici: le CryptoStream ). CryptoStream supprime également automatiquement le Stream encapsulé (ici: le MemoryStream ).
Ainsi, votre MemoryStream est éliminé à la fois par les instructions CryptoStream et à l'aide de . Et votre CryptoStream est éliminé par les instructions StreamWriter et externe à l'aide de .
Après quelques expérimentations, il semble impossible de se débarrasser complètement des avertissements. Théoriquement, le MemoryStream doit être supprimé, mais vous ne pouvez théoriquement plus accéder à sa méthode ToArray. Pratiquement, un MemoryStream n'a pas besoin d'être éliminé, donc j'irais avec 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 directives du .NET Framework recommandent d'implémenter IDisposable.Dispose de telle manière qu'il puisse être appelé plusieurs fois. De la description MSDN de IDisposable.Dispose :
L'objet ne doit pas lever d'exception si sa méthode Dispose est appelée plusieurs fois
Par conséquent, l'avertissement semble presque vide de sens:
Pour éviter de générer une System.ObjectDisposedException, vous ne devez pas appeler Dispose plus d'une fois sur un objet
Je suppose que l'on pourrait soutenir que l'avertissement peut être utile si vous utilisez un objet IDisposable mal implémenté qui ne suit pas les directives d'implémentation standard. Mais lorsque vous utilisez des classes du .NET Framework comme vous le faites, je dirais qu'il est sûr de supprimer l'avertissement à l'aide d'un #pragma. Et à mon humble avis, il est préférable de passer par des cercles comme suggéré dans la documentation MSDN pour cet avertissement .
J'étais confronté à des problèmes similaires dans mon code.
On dirait que tout CA2202 est déclenché car MemoryStream
peut être supprimé si une 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 renvoyer le memoryStream
à l'intérieur de la dernière instruction using
(ligne 10) car cryptoStream
est supprimé à la ligne 11 (car il est utilisé dans streamWriter
using
), ce qui conduit memoryStream
à être également supprimé à la ligne 11 (car memoryStream
est utilisé pour créer le cryptoStream
).
Au moins, ce code a fonctionné pour moi.
MODIFIER:
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.
Le cryptostream est basé sur le memorystream.
Ce qui semble se produire, c'est que lorsque le flux cryogénique est disposé (à la fin de l'utilisation), le flux mémoire est également supprimé, puis le flux mémoire est à nouveau supprimé.
Je voulais résoudre ce problème de la bonne façon - c'est-à-dire sans supprimer les avertissements et éliminer correctement tous les objets jetables.
J'ai retiré 2 des 3 flux en tant que champs et les ai déposés dans la méthode Dispose()
de ma classe. Oui, l'implémentation de l'interface IDisposable
n'est pas nécessairement ce que vous recherchez, mais la solution semble assez propre par rapport aux appels dispose()
de tous les endroits aléatoires du 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();
}
}
}
}
Évitez tous les usages et utilisez les appels Dispose imbriqué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 afin que nous puissions 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 résistent (espérons-le) à l'erreur de plusieurs appels à .Dispose
, pas toutes les classes sont aussi défensives contre une mauvaise utilisation du programmeur.
FX Cop le sait et vous avertit.
Vous avez quelques choix;
Dispose
qu'une seule fois sur n'importe quel objet; n'utilisez pas using
Hors sujet, mais je vous suggère d'utiliser une technique de formatage différente pour regrouper les 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 préconise également d'utiliser var
s ici pour éviter les répétitions de noms de classe très longs.
P.S. Merci à @Shellshock d'avoir souligné que je ne peux pas omettre les accolades pour le premier using
car cela ferait memoryStream
dans return
instruction hors de la portée.