CA2202,如何解决这种情况

tes*_*ino 101 .net c# code-analysis fxcop

任何人都可以告诉我如何从以下代码中删除所有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();
    }
}
Run Code Online (Sandbox Code Playgroud)

警告7 CA2202:Microsoft.Usage:对象'cryptoStream'可以在方法'CryptoServices.Encrypt(string,byte [],byte [])'中多次处理.为避免生成System.ObjectDisposedException,不应在对象上多次调用Dispose:Lines:34

警告8 CA2202:Microsoft.Usage:对象'memoryStream'可以在方法'CryptoServices.Encrypt(string,byte [],byte [])'中多次处理.为避免生成System.ObjectDisposedException,不应在对象上多次调用Dispose:Lines:34,37

您需要Visual Studio代码分析才能看到这些警告(这些不是c#编译器警告).

Jor*_*dão 139

在这种情况下,您应该取消警告.处理一次性用品的代码应该是一致的,您不应该关心其他类别对您创建的一次性用品的所有权并且也要求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();
  }
}
Run Code Online (Sandbox Code Playgroud)

更新:IDisposable.Dispose文档中,您可以阅读:

如果多次调用对象的Dispose方法,则该对象必须忽略第一个之后的所有调用.如果多次调用Dispose方法,则该对象不得抛出异常.

可以说这个规则的存在是为了让开发人员能够using在一系列一次性用品中巧妙地使用这个陈述,就像我上面所示(或者这可能只是一个很好的副作用).出于同样的原因,CA2202没有任何用处,应该在项目方面进行抑制.真正的罪魁祸首是错误的实施Dispose,CA1065应该照顾(如果它是你的责任).

  • 在我看来这是fxcop中的一个错误,这条规则完全错了.dispose方法永远不应该抛出ObjectDisposedException,如果它确实存在,那么你应该通过在这样实现dispose的代码的作者上填写一个bug来处理它. (14认同)
  • 我在另一个帖子中同意@HansPassant:该工具正在完成其工作并警告您类的意外实现细节.就个人而言,我认为真正的问题是API本身的设计.让嵌套类默认假设可以获得在别处创建的另一个对象的所有权似乎非常值得怀疑.我可以看到如果返回结果对象可能有用的地方,但违反该假设似乎是违反直觉的,以及违反正常的IDisposable模式. (14认同)
  • 但msdn并不建议Supress这种类型的消息.看看:https://msdn.microsoft.com/en-us/library/ms182334.aspx?f = 255&MSPPError = -2147217396 (8认同)
  • 感谢您提供@AdilMammadov链接,该链接提供了有用的信息,但Microsoft并不总是正确地处理这些事情。 (2认同)

Han*_*ant 41

嗯,这是准确的,这些流上的Dispose()方法将被多次调用.StreamReader类将获取cryptoStream的"所有权",因此处理streamWriter也将处理cryptoStream.类似地,CryptoStream类接管memoryStream的责任.

这些并不是真正的错误,这些.NET类对多个Dispose()调用具有弹性.但是如果你想摆脱警告,那么你应该删除这些对象的using语句.如果代码抛出异常,在推理会发生什么时会让自己痛苦一点.或者使用属性关闭警告.或者只是忽略警告,因为它很愚蠢.

  • 必须具备关于类的内部行为的特殊知识(比如一次性取得另一个的所有权),要求是否想要设计可重用的API太多了.所以我认为`using`语句应该保留.这些警告实在太傻了. (9认同)
  • 我同意.但是,我仍然不会删除`using`语句.依靠另一个对象来处理我创建的对象是错误的.对于这段代码,没关系,但是那里有很多`Stream`和`TextWriter`的实现(不仅仅是在BCL上).使用它们的代码应该是一致的. (8认同)
  • @HansPassant你能指出`XmlDocument.Save()`方法会在提供的参数上调用`Dispose`的地方吗?我没有在[`Save(XmlWriter)`](http://msdn.microsoft.com/en-us/library/z2w98a50.aspx)的文档中看到它(我遇到了FxCop错误),或者在[`Save()`](http://msdn.microsoft.com/en-us/library/System.Xml.XmlDocument.Save.aspx)方法本身,或者在[`XmlDocument`]的文档中(http://msdn.microsoft.com/en-us/library/System.Xml.XmlDocument.aspx)本身. (4认同)
  • @Jordão - 这不是工具的用途吗?警告你可能不知道的内部行为? (3认同)
  • 是的,同意Jordão.如果您真的希望程序员知道api的内部行为,那么请将您的函数命名为DoSomethingAndDisposeStream(Stream stream,OtherData data). (3认同)

dtb*_*dtb 9

当处理StreamWriter时,它将自动处理包装的Stream(此处:CryptoStream).CryptoStream还会自动处理包装的Stream(此处为:MemoryStream).

因此,您的MemoryStreamCryptoStreamusing语句处理.并且您的CryptoStreamStreamWriter和外部using语句处理.


经过一些实验,似乎不可能完全摆脱警告.理论上,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();
Run Code Online (Sandbox Code Playgroud)


Joe*_*Joe 9

我会这样做#pragma warning disable.

.NET Framework指南建议以可以多次调用的方式实现IDisposable.Dispose.来自IDisposable.Dispose的MSDN描述:

如果多次调用Dispose方法,则该对象不得抛出异常

因此警告似乎几乎毫无意义:

为避免生成System.ObjectDisposedException,不应在对象上多次调用Dispose

我想可以认为,如果您使用的是严格执行的IDisposable对象,并且不符合标准实施指南,则警告可能会有所帮助.但是当你使用.NET Framework中的类时,我会说使用#pragma来抑制警告是安全的.恕我直言,这比在这个警告的MSDN文档中建议的箍更可取.

  • CA2202是代码分析警告,而不是编译器警告.`#pragma warning disable`只能用于抑制编译器警告.要禁止代码分析警告,您需要使用属性. (4认同)

Hen*_*rik -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();
        }
    }
Run Code Online (Sandbox Code Playgroud)

编辑回应评论:我刚刚再次验证此代码不会生成警告,而原始代码会生成警告。在原始代码中,CryptoStream.Dispose()MemoryStream().Dispose() 实际上被调用了两次(这可能是问题,也可能不是问题)。

修改后的代码工作原理如下:null一旦处置责任转移到另一个对象,引用就会设置为 。例如,在调用构造函数成功后memoryStream设置为。调用构造函数成功后设置为。如果没有发生异常,会在块中被释放,并且会依次释放和。nullCryptoStreamcryptoStreamnullStreamWriterstreamWriterfinallyCryptoStreamMemoryStream

  • -1 为了遵守[应该被抑制]的警告而创建丑陋的代码真的很糟糕(http://stackoverflow.com/questions/3831676/ca2202-how-to-solve-this-case/3839419#3839419) 。 (86认同)
  • 我同意你不应该为了那些可能在未来某个时候被修复的东西而浪费你的代码,而只是抑制。 (4认同)
  • 这是如何解决问题的呢?仍然会报告 CA2202,因为 memoryStream 仍然可以在 finally 块中处置两次。 (3认同)
  • 由于 CryptoStream 在内部调用 MemoryStream 上的 Dispose,因此可能会调用两次,这就是出现警告的原因。我尝试了你的解决方案,但仍然收到警告。 (3认同)
  • 哦天哪,你是对的——我没想到会有清理逻辑与你的……逻辑逻辑……混合在一起——这只是奇怪和神秘的——这当然很聪明——但是,再说一次,可怕——请不要在生产代码中这样做;需要明确的是:您确实知道这没有解决任何实际的功能问题,对吗?(多次处理这些对象是可以的。) - 如果可以的话,我会删除反对票(所以阻止我,它说你必须编辑答案) - 但我只会不情愿地这样做...... -说真的,永远不要这样做。 (2认同)