Streams


Serialization is the ability to read and write an arbitrary object graph (reading is sometimes called deserialization ). Before we can talk about serializing objects, we need to talk about where they're going serialized to.

Whenever an object is serialized, it must go somewhere. It may go into memory, a file, a database record, or a socket. Generally, where the data is actually written doesn't matter to the object itself. It needs to store the same data regardless of where it goes. All the object generally cares about is that bytes can be written and read, and sometimes we'd like to skip around among the bytes. To satisfy these desires, .NET provides the abstract base class Stream from the System.IO namespace:

 abstract class  Stream  : MarshalByRefObject, IDisposable {   // Fields   public static readonly System.IO.Stream Null;   // Properties   public bool CanRead { virtual get; }   public bool CanSeek { virtual get; }   public bool CanWrite { virtual get; }   public long Length { virtual get; }   public long Position { virtual get; virtual set; }   // Methods   public virtual IAsyncResult BeginRead(...);   public virtual IAsyncResult BeginWrite(...);   public virtual void Close();   public virtual int EndRead(IAsyncResult asyncResult);   public virtual void EndWrite(IAsyncResult asyncResult);   public virtual void Flush();  public virtual int Read(byte[] buffer, int offset, int count);   public virtual int ReadByte();   public virtual long Seek(long offset, System.IO.SeekOrigin origin);  public virtual void SetLength(long value);  public virtual void Write(byte[] buffer, int offset, int count);   public virtual void WriteByte(byte value);  } 

.NET provides several classes that derive from Stream, including MemoryStream, FileStream, and IsolatedStorageFileStream. The MemoryStream class is fun to play with because it has no permanent side effects:

  using System.IO;  ... string s = "Wahoo!"; int n = 452;  using( Stream stream = new MemoryStream() ) {  // Write to the stream   byte[] bytes1 = UnicodeEncoding.Unicode.GetBytes(s);   byte[] bytes2 = BitConverter.GetBytes(n);   stream.Write(bytes1, 0, bytes1.Length);   stream.Write(bytes2, 0, bytes2.Length);   // Reset the stream to the beginning   stream.Seek(0, SeekOrigin.Begin);   // Read from the stream   byte[] bytes3 = new byte[stream.Length - 4];   byte[] bytes4 = new byte[4];   stream.Read(bytes3, 0, bytes3.Length);   stream.Read(bytes4, 0, bytes4.Length);   // Do something with the data   MessageBox.Show(UnicodeEncoding.Unicode.GetString(bytes3) + " " +     BitConverter.ToInt32(bytes4, 0));  }  

This code creates a specific implementation of the abstract Stream class, making sure to close it (even in the face of exceptions). The code then uses the stream for writing and reading bytes, being careful to seek back to the beginning of the stream in between these actions. We could have written exactly the same code for any stream.

However, the manual conversion of the string object back and forth between the bytes is kind of a pain. To avoid writing that code, we've got the StreamWriter and StreamReader classes:

 string s = "Wahoo!"; int n = 452; using( Stream stream = new MemoryStream() ) {  // Write to the stream   StreamWriter writer = new StreamWriter(stream);   writer.WriteLine(s);   writer.WriteLine(n);   writer.Flush(); // Flush the buffer  // Reset the stream to the beginning   stream.Seek(0, SeekOrigin.Begin);  // Read from the stream   StreamReader reader = new StreamReader(stream);   string s2 = reader.ReadLine();   int n2 = int.Parse(reader.ReadLine());  // Do something with the data   MessageBox.Show(s2 + " " + n2); } 

This code is considerably simpler because the conversion to bytes is managed by the stream writer and readers as they work on the stream. However, the stream writer and readers are oriented toward text only, and that's why we wrote each piece of data on its own line and why we had to parse the integer back out of the string when reading. To avoid the conversion to and from strings, we can write the data in its native binary format using the BinaryWriter and BinaryReader classes:

 string s = "Wahoo!"; int n = 452; using( Stream stream = new MemoryStream() ) {   // Write to the stream  BinaryWriter writer = new BinaryWriter(stream);  writer.Write(s);   writer.Write(n);   writer.Flush(); // Flush the buffer   // Reset the stream to the beginning   stream.Seek(0, SeekOrigin.Begin);   // Read from the stream  BinaryReader reader = new BinaryReader(stream);   string s2 = reader.ReadString();   int n2 = reader.ReadInt32();  // Do something with the data   MessageBox.Show(s2 + " " + n2); } 

Using BinaryWriter and BinaryReader eliminates the need for string conversion, but our code still must keep track of the types of the objects we are writing and the order in which they must be read. We can group the data into a custom class and read it all at once, but BinaryWriter and BinaryReader don't support custom classes, only built-in simple types. To read and write arbitrary objects, we need a formatter.



Windows Forms Programming in C#
Windows Forms Programming in C#
ISBN: 0321116208
EAN: 2147483647
Year: 2003
Pages: 136
Authors: Chris Sells

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net