某项目需要输出一个数据文件,该文件由2部分组成,即文件头信息和数据。
项目是使用C#语言在.NET Framework 4上创建的。
拿到这个需求,首先想到的是定义一个Writer类,在写入方法中创建一个文件流,使用BinaryWriter封装,写入所需要的各种数据。看起来就像这样:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<code>public</code> <code>void</code> <code>Write()</code>
<code>{</code>
<code> </code><code>using</code> <code>(Stream stream = OpenFileStream())</code>
<code> </code><code>using</code> <code>(BinaryWriter writer = </code><code>new</code> <code>BinaryWriter(stream))</code>
<code> </code><code>{</code>
<code> </code><code>WriteHeader(writer);</code>
<code> </code><code>WriteData(writer);</code>
<code> </code><code>}</code>
<code>}</code>
<code>private</code> <code>void</code> <code>WriteHeader(BinaryWriter writer)</code>
<code> </code><code>// ......</code>
<code>private</code> <code>void</code> <code>WriteData(BinaryWriter writer)</code>
不过随后需求就发生了变化,因为数据敏感,需要加密。于是想到对文件头部分使用BinaryWriter写入,而后面的数据部分,先使用CryptoStream包装流,再使用BinaryWriter写入。于是改成这样:
<code> </code><code>using</code> <code>(BinaryWriter headerWriter = </code><code>new</code> <code>BinaryWriter(stream))</code>
<code> </code><code>{</code>
<code> </code><code>WriteHeader(headerWriter);</code>
<code> </code><code>}</code>
<code> </code><code>using</code> <code>(CryptoStream cryptoStream</code>
<code> </code><code>= </code><code>new</code> <code>CryptoStream(stream, GetCryptoTransform(),</code>
<code> </code><code>CryptoStreamMode.Write))</code>
<code> </code><code>using</code> <code>(BinaryWriter dataWriter = </code><code>new</code> <code>BinaryWriter(cryptoStream))</code>
<code> </code><code>WriteData(dataWriter);</code>
改过之后,问题产生了——在使用headerWriter的using语句结束时,会自动调用headerWriter.Dispose(),而这个方法会调用BaseStream.Close(),也就是说,文件流被关闭了,那么后面尝试写入数据时就会抛出异常。
虽然BinaryWriter有一个构造方法可以申明不关闭流:
<code>public</code> <code>BinaryWriter(</code>
<code> </code><code>Stream output,</code>
<code> </code><code>Encoding encoding,</code>
<code> </code><code>bool</code> <code>leaveOpen</code>
<code>)</code>
但这个构造方法是在.NET 4.5才加入的,项目是用的4.0的Framework,所以必须另外想办法。而且后面的CryptoStream也存在同样的问题,而它可没有提供不关闭流的构造。
这里可以想到两个办法来处理:
1. 在所有内容都写完之后再统一Dispose各种操作对象和流对象。
2. 定义一个从Stream继承的StreamWrapper,将Close和Dispose都重载并实现为空方法,再定义一个ReallyClose方法来真正关闭封装的流。
使用第1种方法,就像这样:
<code> </code><code>using</code> <code>(Stream s = OpenFileStream())</code>
<code> </code><code>using</code> <code>(BinaryWriter headWriter = </code><code>new</code> <code>BinaryWriter(s))</code>
<code> </code><code>using</code> <code>(CryptoStream cs = </code><code>new</code> <code>CryptoStream(s, GetCryptoTransform(),</code>
<code> </code><code>CryptoStreamMode.Write))</code>
<code> </code><code>using</code> <code>(BinaryWriter dataWriter = </code><code>new</code> <code>BinaryWriter(cs))</code>
<code> </code><code>WriterHeader(headWriter);</code>
<code> </code><code>WriteData(dataWriter);</code>
而且如果一个文件分成了很多很多段的话,这个using列表就太长了。但这不是问题,问题是如果WriteHeader中抛出异常,那么cs和dataWriter这两个对象就浪费了。所以,可以考虑使用try {...} finally {...} 来实现using,
18
19
20
21
22
23
<code> </code><code>Stream stream = </code><code>null</code><code>;</code>
<code> </code><code>BinaryWriter headWriter = </code><code>null</code><code>;</code>
<code> </code><code>CryptoStream cs = </code><code>null</code><code>;</code>
<code> </code><code>BinaryWriter dataWriter = </code><code>null</code><code>;</code>
<code> </code><code>try</code>
<code> </code><code>stream = OpenFileStream();</code>
<code> </code><code>headWriter = </code><code>new</code> <code>BinaryWriter(stream);</code>
<code> </code><code>cs = </code><code>new</code> <code>CryptoStream(stream, GetCryptoTransform(), CryptoStreamMode.Write);</code>
<code> </code><code>dataWriter = </code><code>new</code> <code>BinaryWriter(cs);</code>
<code> </code><code>finally</code>
<code> </code><code>if</code> <code>(dataWriter != </code><code>null</code><code>) { dataWriter.Dispose(); }</code>
<code> </code><code>if</code> <code>(cs != </code><code>null</code><code>) { cs.Dispose(); }</code>
<code> </code><code>if</code> <code>(headWriter != </code><code>null</code><code>) { headWriter.Dispose(); }</code>
<code> </code><code>if</code> <code>(stream != </code><code>null</code><code>) { stream.Dispose(); }</code>
解决问题,但代码量大,而且容易出错。比如,要记得在WriterHeader里面Flush,这个原本会在Dispose()自动执行的操作(对于CryptoStream,需要执行FlushFinalBlock())。
相对来说,写一个StreamWrapper靠谱多了。不过使用RealClose就失去了IDisposable的意义,所以稍稍改变一下,定义一个变量来允许Dispose时关闭流。
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
<code>class</code> <code>StreamWrapper : Stream</code>
<code> </code><code>private</code> <code>readonly</code> <code>Stream stream;</code>
<code> </code><code>public</code> <code>Stream Stream { </code><code>get</code> <code>{ </code><code>return</code> <code>stream; } }</code>
<code> </code><code>public</code> <code>StreamWrapper(Stream stream)</code>
<code> </code><code>this</code><code>.stream = stream;</code>
<code> </code><code>IsLeavingOpen = </code><code>true</code><code>;</code>
<code> </code><code>public</code> <code>override</code> <code>void</code> <code>Flush()</code>
<code> </code><code>stream.Flush();</code>
<code> </code><code>public</code> <code>override</code> <code>long</code> <code>Seek(</code><code>long</code> <code>offset, SeekOrigin origin)</code>
<code> </code><code>return</code> <code>stream.Seek(offset, origin);</code>
<code> </code><code>public</code> <code>override</code> <code>void</code> <code>SetLength(</code><code>long</code> <code>value)</code>
<code> </code><code>stream.SetLength(value);</code>
<code> </code><code>public</code> <code>override</code> <code>int</code> <code>Read(</code><code>byte</code><code>[] buffer, </code><code>int</code> <code>offset, </code><code>int</code> <code>count)</code>
<code> </code><code>return</code> <code>stream.Read(buffer, offset, count);</code>
<code> </code><code>public</code> <code>override</code> <code>void</code> <code>Write(</code><code>byte</code><code>[] buffer, </code><code>int</code> <code>offset, </code><code>int</code> <code>count)</code>
<code> </code><code>stream.Write(buffer, offset, count);</code>
<code> </code><code>public</code> <code>override</code> <code>bool</code> <code>CanRead { </code><code>get</code> <code>{ </code><code>return</code> <code>stream.CanRead; } }</code>
<code> </code><code>public</code> <code>override</code> <code>bool</code> <code>CanSeek { </code><code>get</code> <code>{ </code><code>return</code> <code>stream.CanSeek; } }</code>
<code> </code><code>public</code> <code>override</code> <code>bool</code> <code>CanWrite { </code><code>get</code> <code>{ </code><code>return</code> <code>stream.CanWrite; } }</code>
<code> </code><code>public</code> <code>override</code> <code>long</code> <code>Length { </code><code>get</code> <code>{ </code><code>return</code> <code>stream.Length; } }</code>
<code> </code><code>public</code> <code>override</code> <code>long</code> <code>Position</code>
<code> </code><code>get</code> <code>{ </code><code>return</code> <code>stream.Position; }</code>
<code> </code><code>set</code> <code>{ stream.Position = value; }</code>
<code> </code><code>public</code> <code>override</code> <code>void</code> <code>Close()</code>
<code> </code><code>if</code> <code>(IsLeavingOpen)</code>
<code> </code><code>return</code><code>;</code>
<code> </code><code>stream.Close();</code>
<code> </code><code>base</code><code>.Close();</code>
<code> </code><code>protected</code> <code>override</code> <code>void</code> <code>Dispose(</code><code>bool</code> <code>disposing)</code>
<code> </code><code>if</code> <code>(disposing)</code>
<code> </code><code>stream.Dispose();</code>
<code> </code><code>base</code><code>.Dispose(disposing);</code>
<code> </code><code>public</code> <code>bool</code> <code>IsLeavingOpen { </code><code>get</code><code>; </code><code>set</code><code>; }</code>
如果稍稍改变一下写入数据的接口,使用起来也非常方便
<code> </code><code>using (Stream fileStream = OpenFileStream())</code>
<code> </code><code>using (StreamWrapper stream = </code><code>new</code> <code>StreamWrapper(fileStream))</code>
<code> </code><code>WriteHeader(stream);</code>
<code> </code><code>WriteData(stream);</code>
<code> </code><code>stream.IsLeavingOpen = </code><code>false</code><code>;</code>
<code>private</code> <code>void</code> <code>WriteHeader(Stream stream)</code>
<code> </code><code>using (BinaryWriter writer = </code><code>new</code> <code>BinaryWriter(stream))</code>
<code> </code><code>// ......</code>
<code>private</code> <code>void</code> <code>WriteData(Stream stream)</code>
<code> </code><code>using (CryptoStream cryptoStream = </code><code>new</code> <code>CryptoStream(stream,</code>
<code> </code><code>GetCryptoTransform(), CryptoStreamMode.Write))</code>
<code> </code><code>using (BinaryWriter writer = </code><code>new</code> <code>BinaryWriter(cryptoStream))</code>
<code></code>
本文转自边城__ 51CTO博客,原文链接:http://blog.51cto.com/jamesfancy/1359515,如需转载请自行联系原作者