为什么visual studio代码分析警告我这个对象可以在这段代码中被处理两次(当它不能时)

时间:2012-06-05 23:18:19

标签: c# visual-studio-2010

代码是:

using (MemoryStream memorystream = new MemoryStream(bytes))
{
    using (BinaryWriter writer = new BinaryWriter(memorystream))
    {
        writer.Write((double)100.0);
    }
    return memorystream.ToArray();
}

以上代码是否适合妥善处理这两个对象?

代码分析是否有用?除了关于变量名称和名称空间的垃圾信息之外,它似乎抱怨很多事情都不是现实。我真的在想,也许它很有用,我只是忽略了这一点。

OKAY解决关于MemoryStream是否被丢弃的问题(不是)这里是VS代码分析给出完全相同警告的示例。显然,这里没有任何东西被处理掉

public class MyClass : IDisposable
{
   public void DoSomethingElse()
    {

    }

   #region IDisposable Members
   public void Dispose()
   {
       throw new NotImplementedException();
   }
   #endregion
}

public class MyOtherClass : IDisposable
{
    public MyOtherClass(MyClass mc)
    {

    }
    public void DoSomething() { }
}

public void Foo()
{
    using (MyClass mc = new MyClass())
    {
        using (MyOtherClass otherclass = new MyOtherClass(mc))
        {
            otherclass.DoSomething();
        }
        mc.DoSomethingElse();
    }
}

5 个答案:

答案 0 :(得分:0)

On“如果流被处理掉了,那我该如何调用memorystream.ToArray()”问题的一部分:

MemeoryStream.Dispose不释放内部缓冲区。它唯一能做的就是通过抛出“Object Disposed”异常来阻止所有Stream方法(比如Read)。即以下代码完全有效(通常可以为管理某种存储的其他其他Stream对象编写访问较低级别存储的类似代码):

MemoryStream memoryStream = new MemoryStream();
using (memoryStream)
{
 // write something to the stream
}
// Note memoryStream.Write will fail here with an exception,
// only calls to get storage (ToArray and GetBuffer) make sense at this point.
var data = memoryStream.ToArray();

答案 1 :(得分:0)

如果我猜测我会说分析规则的逻辑是确定一种可能性如下:

“您正在将一次性对象传递到另一个一次性对象的构造函数中(因此在语义上意味着所有权的转移)。如果所有权真正被转移到第二个对象,则很可能它将处置该对象在它自己的Dispose方法中传递给它的构造函数,但你也要处理它。“

你的第二个例子证明它实际上并没有分析第二个对象是否取得了所有权并处置了第一个,而是这个规则说这个模式有一些语义歧义,作为负责处理第一个对象的代码不再清楚了。

dispose模式具有alloc / free和new / delete的所有固有缺陷,因为只有一个类应该拥有,从而控制一次性实例的生命周期。

当然,所有纯粹的猜想,但这就是我读它的方式。

答案 2 :(得分:0)

MemoryStream包装一个托管缓冲区,该缓冲区将来会在某个时刻被垃圾收集,无论是否被丢弃。

IDisposable实施是Stream合同的一部分。它会关闭流,阻止您使用Read和Write方法,但仍允许您访问基础字节。 (防止调用ToArray可能会很难与StreamWrite一起使用。)使用非托管资源(如FileStream)的Stream实现会在处理文件时释放文件句柄。

代码分析注意到你和StreamWriter都在同一个对象上调用Dispose并警告它。这对于MemoryStream来说毫无意义,但对于其他一些IDisposable实现可能会很危险。

您似乎将IDisposable接口与垃圾回收混淆。调用Dispose不会释放托管对象,它只是让您的类有机会以确定的方式释放非托管资源。

答案 3 :(得分:0)

BinaryWriter不会释放MemoryStream。你需要处理它。在变量中获取.ToArray的结果,然后释放MemoryStream,然后返回结果。或者使用try / finally来处理MemoryStream。

答案 4 :(得分:-1)

我认为BinaryWriter.Dispose()会调用其基础MemoryStream的{​​{1}},然后您的.Dispose()会重新处理它。

至少我认为这将会发生什么。我没有打开using进行验证的来源,但我总是认为它关闭并处理了它的基础流。


编辑:

这是课程的来源。您可以看到内存流的处理位置:

BinaryStream

BinaryWriter

的MemoryStream

    public BinaryWriter(Stream output) : this(output, new UTF8Encoding(false, true)) 
    {
    } 

    public BinaryWriter(Stream output, Encoding encoding)
    { 
        if (output==null)
            throw new ArgumentNullException("output");
        if (encoding==null)
            throw new ArgumentNullException("encoding"); 
        if (!output.CanWrite)
            throw new ArgumentException(Environment.GetResourceString("Argument_StreamNotWritable")); 
        Contract.EndContractBlock(); 

        OutStream = output; 
        _buffer = new byte[16];
        _encoding = encoding;
        _encoder = _encoding.GetEncoder();
    } 

    protected virtual void Dispose(bool disposing)
    { 
        if (disposing) 
            OutStream.Close();
    } 

    public class MemoryStream : Stream
    {
        ....
    }

为了回答你的另一个问题,“为什么你仍然可以调用.ToArray(),如果已经处理了MemoryStream”,那么这个测试通过就好了:

    public virtual void Close()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

因此在处置后仍然可以访问 [TestMethod] public void TestDispose() { var m = new MemoryStream(); m.WriteByte(120); m.Dispose(); var a = m.ToArray(); Assert.AreEqual(1, a.Length); } 方法。

字节仍然可用。 .ToArray() dispose只是在内部设置了一些标志,阻止你进一步修改流:

MemoryStream

事实上,请注意来源中有注释:

    protected override void Dispose(bool disposing)
    { 
        try {
            if (disposing) {
                _isOpen = false;
                _writable = false; 
                _expandable = false;
                // Don't set buffer to null - allow GetBuffer & ToArray to work. 
            } 
        }
        finally { 
            // Call base.Close() to cleanup async IO resources
            base.Dispose(disposing);
        }
    } 

这样才真正回答了这个问题:)