2013-04-24 104 views
3

我有一個非封閉的流類,封裝在帶有二進制閱讀器的使用塊中,但由於某些原因塊結束時,我的非封閉流仍然關閉。爲什麼我的非關閉流仍然關閉?

流被定義爲:

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); 
    }; 

    ... 
} 

拉姆達從外部塊關閉,而不是使用緩存的解串器運行時提供的bReader超過reader。因此,當反序列化器運行時,它使用的是已經廢棄的二進制閱讀器對象,而不是提供給它的新對象。我想這個問題不是我關閉了這個流,而是我使用了一個處理過的閱讀器。至少這解釋了爲什麼它會工作一次,然後第二次失敗,因爲第二次它依賴於緩存的解串器。哎呀!

謝謝大家。

+2

在這裏看不到任何問題。你怎麼知道它已關閉?你怎麼知道它已經關閉*在這裏*? – usr 2013-04-24 21:54:32

+0

因爲如果我打開它們,它工作正常。 – sircodesalot 2013-04-24 21:56:12

+0

您如何知道基礎流已關閉?之後你在做什麼?讀取序列化對象後,流中是否有更多數據? – antlersoft 2013-04-24 21:56:47

回答

0

它取決於您的NonClosingStream類包裝的流是否在其他地方被引用。如果不是,那麼基礎流將沒有引用,因此在某個時刻,其終結器將關閉流。

+0

如果沒有進一步的引用,他不能*觀察*正在關閉的流。 – usr 2013-04-24 21:58:04

+0

他可以,如果它是現在可訪問的文件,因爲不再有任何與它關聯的進程。 – 2013-04-24 22:05:48

1

由於您的流不會創建內部流,因此最有可能的外部代碼會關閉您的內部流。機會是你的代碼的樣子:

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

我認爲這是一個評論,因爲它是高度推測。 – usr 2013-04-24 22:00:28

+0

這就是我期待的事情,但它怎麼能爲我關閉流? – sircodesalot 2013-04-24 22:00:36

+0

@usr - 很可能,不幸的是,代碼在評論中是不可讀的:( – 2013-04-24 22:01:06

1
void IDisposable.Dispose() 

你的類有的Dispose()方法。你明確實施的那個。以及您從Stream類繼承的那個。問題是,BinaryStream不知道你的豆子。它只知道Stream實現的那個。此外,當您使用BinaryStream(Stream)構造函數時,BinaryStream對象將承擔傳遞的Stream對象的所有權。這意味着它將在處理它自己時處置該流。也許你現在看到了這個問題,繼承的Dispose()方法將被調用,而不是你的。它關閉了基礎流。

這就是Stream實現Dispose模式的原因。您需要使其看起來像這樣:

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

是的,我最初做到了,但行爲沒有任何區別。以某種方式使我的非關閉流(這沒有意義),它不應該能夠關閉'this.baseStream'。目前不在代碼前面,但我明天再試一次。 – sircodesalot 2013-04-25 03:38:11