【问题标题】:Why does my non closing stream still close?为什么我的非关闭流仍然关闭?
【发布时间】:2013-04-24 21:48:47
【问题描述】:

我有一个非关闭流类,它封装在带有二进制读取器的 using 块中,但由于某种原因,当块结束时,我的非关闭流仍然关闭。

流定义为:

internal class NonClosingStream : Stream, IDisposable
{
    private Stream baseStream;

    public NonClosingStream(Stream baseStream)
    {
        this.baseStream = baseStream;
    }

    public override bool CanRead{ get { return baseStream.CanRead; } }
    public override bool CanSeek{ get { return baseStream.CanSeek; } }
    public override bool CanWrite { get { return baseStream.CanWrite; } }

    public override void Flush()
    {
        baseStream.Flush();
    }

    public override long Length { get { return baseStream.Length; } }

    public override long Position
    {
        get { return baseStream.Position; }
        set { baseStream.Position = value; }
    }

    public override int Read(byte[] buffer, int offset, int count)
    {
        return baseStream.Read(buffer, offset, count);   
    }

    public override long Seek(long offset, SeekOrigin origin)
    {
        return baseStream.Seek(offset, origin);
    }

    public override void SetLength(long value)
    {
        baseStream.SetLength(value);
    }

    public override void Write(byte[] buffer, int offset, int count)
    {
        baseStream.Write(buffer, offset, count);
    }

    public override void Close()
    {
        // Disconnects from base stream, but does not close it
        this.baseStream = null;
    }

    void IDisposable.Dispose()
    {
        // Disconnects from base stream, but does not close it
        this.baseStream = null;
    }
}

读取块如下所示:

public T Deserialize<T>(Stream stream)
{
    using (NonClosingStream nonClosingStream = new NonClosingStream(stream))
    using (BinaryReader reader = new BinaryReader(nonClosingStream, Encoding.ASCII, true))
    {
        // Read the type name, then convert it to an actual type
        String typeName = reader.ReadString();
        Type graphType = AvailableTypes.GetType(typeName);

        // If a deserializer for this type already exists, use it.
        if (deserializerFunctions.ContainsKey(graphType))
        {
            return (T)deserializerFunctions[graphType](reader);
        }

        // Otherwise, create one and use it
        T graph = (T)FormatterServices.GetUninitializedObject(graphType);

        typeof(ServiceSerializer).GetMethod("DeserializeObject",
                BindingFlags.NonPublic | BindingFlags.Static)

            .MakeGenericMethod(graphType)
            .Invoke(this, new Object[] { reader, graph });

        return graph;
    }
}

我做错了什么?

更新

所以我写了这个小家伙:

static void Main()
{
    MemoryStream stream = new MemoryStream();
    using (NonClosingStream nonCloser = new NonClosingStream(stream))
    using (BinaryWriter writer = new BinaryWriter(nonCloser))
    using (BinaryReader reader= new BinaryReader(nonCloser))
    {
        writer.Write("Lorem ipsum");

        stream.Seek(0, SeekOrigin.Begin);
        String data = reader.ReadString();

        Console.WriteLine(data);
    }

    stream.Seek(0, SeekOrigin.Begin);

    using (NonClosingStream nonCloser = new NonClosingStream(stream))
    using (BinaryWriter writer = new BinaryWriter(nonCloser))
    using (BinaryReader reader = new BinaryReader(nonCloser))
    {
        writer.Write("Lorem ipsum");

        stream.Seek(0, SeekOrigin.Begin);
        String data = reader.ReadString();

        Console.WriteLine(data);
    }

    Console.ReadLine();
}

它似乎工作正常,流保持打开状态,就像它应该的那样。所以我认为共识是正确的。不知何故,我正在关闭其他地方的流。当我弄清楚我会发布结果时。谢谢大家。

更新

Gaaaahhh,我发现了问题所在。因此,代码的工作方式是在序列化/反序列化对象时,然后从表达式树构建自定义序列化器,然后对其进行编译,以便将来的序列化更加流畅。这意味着我的代码中到处都是这样的东西:

Action<BinaryReader, Object> assignmentAction = delegate(BinaryReader bReader, Object oGraph)
{
    bReader.ReadByte();      // Read the next action
    bReader.ReadString();    // Read the field name
    bReader.ReadByte();      // Read the field type

    // Call the assignment lambda
    assignmentLambda(reader, deserializerFunctions[primitiveType], (T)oGraph);
};

你听懂了吗?不?我显然也没有。让我们添加一些上下文:

private static void DeserializeObject<T>(BinaryReader reader, T graph)
{
    ...

    Action<BinaryReader, Object> assignmentAction = delegate(BinaryReader bReader, Object oGraph)
    {
        bReader.ReadByte();      // Read the next action
        bReader.ReadString();    // Read the field name
        bReader.ReadByte();      // Read the field type

        // Call the assignment lambda
        assignmentLambda(reader, deserializerFunctions[primitiveType], (T)oGraph);
    };

    ...
}

lambda 正在从外部块关闭 reader,而不是使用缓存反序列化程序运行时提供的 bReader。因此,当反序列化器运行时,它使用的是已经丢弃的二进制读取器对象,而不是提供给它的新读取器对象。我想问题不在于我正在关闭流,而在于我使用的是已处置的阅读器。至少这解释了为什么它会工作一次,然后第二次失败,因为第二次它依赖于缓存的反序列化器。糟糕!

谢谢大家。

【问题讨论】:

  • 这里看不出任何问题。你怎么知道它被关闭了?你怎么知道它在这里关闭了?
  • 因为如果我打开它们,它就可以正常工作。
  • 怎么知道底层流是关闭的?之后你对它做什么?读取序列化对象后,流上是否还有更多数据?
  • unwrap 是什么意思?我看不到与我的问题的关系。
  • 好的,你怎么知道流在这里被关闭了?您可能正在其他地方关闭流。使用调试器而不是删除 using 以获得更细粒度的结果。

标签: c# serialization stream


【解决方案1】:

由于您的流不会创建内部流,因此外部代码很可能会关闭您的内部流。您的代码可能如下所示:

 NonClosingStream nonClosing;
 using(var stream = new FileStream(...))
 {
   nonClosing = new NonClosingStream(stream );
  ....
 } 
 // inner stream now closed and nonClosing will fail all operations.

【讨论】:

  • 我认为这是一条评论,因为它具有高度投机性。
  • 这就是我所期望的,但它怎么可能为我关闭流?
  • @usr - 不幸的是,cmets 中的代码可能不太可读:(
  • @sircodesalot ? using 在末尾处理/关闭对象 - 因此,如果您有 using 用于传递给 NonClosingStream 的流,则内部流将在 using 块结束时关闭...
  • 但是我的非关闭流将所有流操作传递给baseStream,但我小心不要自己关闭流。这意味着其他东西正在为我关闭它。
【解决方案2】:
void IDisposable.Dispose()

你的类有两个 Dispose() 方法。您明确实施的那个。以及您从 Stream 类继承的那个。问题是,BinaryStream 不知道你的 bean。它只知道 Stream 实现的那个。此外,当您使用 BinaryStream(Stream) 构造函数时,BinaryStream 对象将承担传递的 Stream 对象的所有权。这意味着它会在它自己处置时处置该流。也许您现在看到了问题,继承的 Dispose() 方法将被调用,而不是您的。它会关闭基本流。

这就是 Stream 实现 Dispose 模式的原因。你需要让它看起来像这样:

internal class NonClosingStream : Stream {
    protected override Dispose(bool disposing) {}
}

【讨论】:

  • 是的,我最初是这样做的,但行为没有区别。即使它确实以某种方式关闭了我的非关闭流(这实际上没有意义),它也不应该能够关闭this.baseStream。虽然现在不在代码前面,但我明天再试一次。
【解决方案3】:

这取决于您的 NonClosingStream 类包装的流是否在其他地方引用。如果没有,那么底层流将没有引用,因此在之后的某个时候,它的终结器将关闭该流。

【讨论】:

  • 如果没有进一步的引用,他就无法观察正在关闭的流。
  • 如果它是现在可以访问的文件,他可以这样做,因为不再有任何与之关联的进程。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-06-25
  • 2017-05-19
  • 2012-11-15
相关资源
最近更新 更多