CA2202, как решить этот случай
может кто-нибудь сказать мне, как удалить все 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.Использование : объект потока cryptostream может быть утилизирован более чем один раз в CryptoServices способ '.Шифрование (строка, байт [], байт [])'. Чтобы избежать создания системы.ObjectDisposedException не следует вызывать Dispose более одного раза для объекта.: Lines: 34
предупреждение 8 CA2202: Microsoft.Использование: Объект 'memoryStream 'может быть размещен более одного раза в методе' криптосервисы.Шифрование (строка, байт [], байт [])'. Чтобы избежать создания системы.ObjectDisposedException не следует вызывать Dispose более одного раза для объекта.: Строк: 34, 37
для просмотра этих предупреждений необходим анализ кода Visual Studio (это не предупреждения компилятора c#).
12 ответов:
это компилируется без предупреждения:
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(); } }
Edit в ответ на замечания: Я только что снова проверил, что этот код не генерирует предупреждение, в то время как исходный делает. В исходном коде
CryptoStream.Dispose()
иMemoryStream().Dispose(
) фактически вызываются дважды (что может быть или не быть проблемой).измененный код работает следующим образом: ссылки для
null
, а как только ответственность за утилизацию переносится на другой объект. Например.memoryStream
установлено значениеnull
после вызоваCryptoStream
конструктор удалось.cryptoStream
установлено значениеnull
, после обращения кStreamWriter
конструктор удалось. Если исключение не происходит,streamWriter
расположена вfinally
блок и, в свою очередь, распоряжатьсяCryptoStream
иMemoryStream
.
вы должны подавить предупреждения в этом случае. Код, который имеет дело с disposables, должен быть последовательным, и вам не нужно заботиться о том, чтобы другие классы брали на себя ответственность за созданные вами disposables, а также вызывали
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(); } }
обновление: на IDisposable.Утилизировать документация вы можете прочитать это:
если метод Dispose объекта вызывается более одного раза, объект должен игнорировать все вызовы после первый. Объект не должен бросать исключение, если метод Dispose вызывается несколько раз.
можно утверждать, что это правило существует, чтобы разработчики могли использовать
using
заявление здраво в каскаде disposables, как я показал выше (или, может быть, это просто хороший побочный эффект). Таким же образом, CA2202 не служит никакой полезной цели, и его следует подавлять по проекту. Настоящим виновником была бы ошибочная реализацияDispose
, и CA1065 стоит позаботиться об этом (если это под вашу ответственность).
Ну, это точно, метод Dispose () на этих потоках будет вызываться несколько раз. Класс StreamReader возьмет на себя "владение" cryptoStream, поэтому disposing streamWriter также будет распоряжаться cryptoStream. Аналогично, класс CryptoStream берет на себя ответственность за memoryStream.
Это не совсем реальные ошибки, эти классы .NET устойчивы к нескольким вызовам Dispose (). Но если вы хотите избавиться от предупреждения, то вы должны отказаться от использования оператор для этих объектов. И себе больно немного, когда рассуждаете, что будет, если код генерирует исключение. Или заткните предупреждение с атрибутом. Или просто игнорировать предупреждение, так как это глупо.
Когда a StreamWriter утилизируется, он автоматически утилизирует завернутый поток (здесь: the CryptoStream). CryptoStream также автоматически удаляет обернутое поток (здесь: the 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();
Я бы сделал это с помощью
#pragma warning disable
.интернет .NET и руководящие принципы рекомендуют, чтобы реализовать IDisposable.Утилизируйте таким образом, чтобы его можно было вызвать несколько раз. От описание MSDN IDisposable.Утилизировать:
объект не должен вызывать исключение, если его метод Dispose вызывается несколько раз
поэтому предупреждение кажется почти бессмысленным:
чтобы избежать генерации a Система.ObjectDisposedException вы не должны вызывать Dispose более одного раза на объекте
Я думаю, можно утверждать, что предупреждение может быть полезно, если вы используете плохо реализованный объект IDisposable, который не следовать стандартам реализации. Но при использовании классов из .NET Framework, как вы делаете, я бы сказал, что безопасно подавлять предупреждение с помощью #pragma. И ИМХО это предпочтительнее, чем проходить через обручи как предложено в документация MSDN для этого предупреждения.
cryptostream основан на memorystream.
что, по-видимому, происходит, так это то, что когда crypostream удаляется (в конце использования), memorystream также удаляется, а затем memorystream снова удаляется.
вне темы, но я бы предложил вам использовать другой метод форматирования для группировки
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(); }
Я также выступаю за использование
var
s здесь, чтобы избежать повторения действительно длинных имен классов.П. С. Спасибо @Shellshock для отметить, что я не могу опустить скобки для первого
using
как быmemoryStream
наreturn
выписка из области видимости.
я столкнулся с аналогичными проблемами в моем коде.
похоже, что вся вещь 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
).по крайней мере этот код работал для меня.
EDIT:
забавно, как это может звучать, я обнаружил, что если вы замените
GetMemoryStream
метод со следующим кодом/// <summary> /// Gets a memory stream. /// </summary> /// <returns>A new memory stream</returns> private static MemoryStream GetMemoryStream() { return new MemoryStream(); }
вы получите тот же результат.
Я хотел решить это правильно - то есть без подавления предупреждений и правильно утилизировать все одноразовые предметы.
Я вытащил 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(); } } } }
я использовал такой код, который принимает byte[] и возвращает byte[] без использования потоков
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); }
таким образом, все, что вам нужно сделать-это преобразование из String в Byte[] с помощью кодировки.
избегайте всех использований и используйте вложенные Dispose-вызовы!
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(); } }
Я просто хотел развернуть код, чтобы мы могли видеть несколько вызовов
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
- продолжайте вызывать dispose дважды, и надеюсь, что код не рухнет
- отключить предупреждение