Chapter 10
Input and Output Classes

Kurt Nørmark ©
Department of Computer Science, Aalborg University, Denmark


Abstract
Previous lecture Next lecture
Index References Contents
This is the lecture about classes for input and output in C#. The most fundamental of these are class Stream and its subclasses. In addition to the Stream classes, C# also supports a number of so-called Reader and Writer classes. These classes are built on (delegate to) a stream, but they do not inherit from a stream. We discuss Text and Binary readers and writers. We also look at string readers and string writers. In addition, we will study the classes that allows us to represent files and directories as objects. Text/char encoding, the Console class, and C# attributes are also touched on. We devote a part of this lecture to a study of serialization, which is a simple and easy means to obtain persist objects. The lecture is finalized with a discussion of the Decorator design pattern, which is relevant for building one stream on top of another stream.


Streams

The Stream Concept
Slide Annotated slide Contents Index
References Textbook 

The concept stream:

A stream is a flow of data from a program to a backing store, or from a backing store to a program

The program can either write to a stream, or read from a stream.

Reading from and writing to a stream

  • Streams and stream processing

    • Reading from or writing to files in secondary memory (disk)

    • Reading from or writing to primary memory (RAM)

    • Connection to the Internet

    • Socket connection between two programs

The abstract class Stream in C#
Slide Annotated slide Contents Index
References Textbook 

The abstract class Stream is the most general stream class in C#

  • The Stream class

    • Provides a generic view on data sources and data destinations

    • Isolates the programmer from operating system details

    • Offers reading and writing of raw, binary data

      • Chunked and sequenced as bytes

      • No char encoding is involved

    • Synchronous reading and writing

      • Read and Write transfer byte arrays

      • A subclass must implement the operations Read and Write

    • Asynchronous reading and writing - (not covered in this lecture)

      • BeginRead and EndRead

      • BeginWrite and EndWrite

      • Rely on Read and Write

    • A stream can be queried for its capabilities

      • CanRead, CanWrite, CanSeek

The most important members in class Stream
Slide Annotated slide Contents Index
References Textbook 

  • int Read (byte[] buf, int pos, int len)

  • int ReadByte()

  • void Write (byte[] buf, int pos, int len)

  • void WriteByte(byte b)

  • bool CanRead

  • bool CanWrite

  • bool CanSeek

  • long Length

  • void Seek (long offset, SeekOrigin org)

  • void Flush ()

  • void Close()

Reference

The static field Null represents a stream without a backing store.

Subclasses of class Stream
Slide Annotated slide Contents Index
References Textbook 

There exists a number of non-abstract subclasses of Stream in the namespace System.IO and in other namespaces

  • System.IO.FileStream

    • Provides a stream backed by a file from the operating system

  • System.IO.BufferedStream

    • Encapsulates buffering around another stream

  • System.IO.MemoryStream

    • Provides a stream backed by RAM memory

  • System.Net.Sockets.NetworkStream

    • Encapsulates a socket connection as a stream

  • System.IO.Compression.GZipStream

    • Provides stream access to compressed data

  • System.Security.Cryptography.CryptoStream

    • Write encrypts and Read decrypts

  • And others...

Example: Filestreams
Slide Annotated slide Contents Index
References Textbook 

Class FileStream is a non-abstract subclass of class Stream

We show the simplest possible write and read programs using class FileStream

Reference

Program: A program that writes bytes corresponding to 'O' 'O' 'P' to a file stream.
using System.IO;

class ReadProg {
  static void Main() {
    Stream s = new FileStream("myFile.bin", FileMode.Create);
    s.WriteByte(79);  // O    01001111
    s.WriteByte(79);  // O    01001111
    s.WriteByte(80);  // P    01010000
    s.Close();
  }
}

Program: The contents of myFile.bin - interpreted as simple ASCII text - after executing the write program.
OOP

Program: A program that reads the written file.
using System;
using System.IO;

class WriteProg {
  static void Main() {
    Stream s = new FileStream("myFile.bin", FileMode.Open);
    int i, j, k, m, n;
    i = s.ReadByte();  // O   79  01001111
    j = s.ReadByte();  // O   79  01001111
    k = s.ReadByte();  // P   80  01010000
    m = s.ReadByte();  // -1  EOF
    n = s.ReadByte();  // -1  EOF

    Console.WriteLine("{0} {1} {2} {3} {4}", i, j, k, m, n);
    s.Close();
  }
}

Program: Console output from the read program.
79 79 80 -1 -1

Class FileStream offers binary input and output

The using control structure
Slide Annotated slide Contents Index
References Textbook 

The using control structure is helpful when we do IO programming

Syntax: The syntax of the using statement C#


using (type variable = initializer)
  body

  • Semantics

    • In the scope of using, bind variable to the value of initializer

    • The type must implement the interface IDisposable

    • Execute body with the established name binding

    • At the end of body do variable.Dispose

      • The Dispose methods in the subclasses of Stream call Close

Reference

Program: The control structure 'using' defined by 'try-finally'.
// The using statement ...

  using (type variable = initializer)
    body

// ... is equivalent to the following try-finally statement

  {type variable = initializer;
    try {
      body
    }
    finally {
      if (variable != null) 
         ((IDisposable)variable).Dispose();
    }
  }   

Program: The simple write-program programmed with 'using'.
using System.IO;

class ReadProg {
  static void Main() {
    using(Stream s = new FileStream("myFile.txt", FileMode.Create)){
      s.WriteByte(79);  // O   01001111
      s.WriteByte(79);  // O   01001111
      s.WriteByte(80);  // P   01010000
    }
  }
}

Program: The simple read-program programmed with 'using'.
using System;
using System.IO;

class WriteProg {
  static void Main() {
    int i, j, k, m, n;

    using(Stream s = new FileStream("myFile.bin", FileMode.Open)){
      i = s.ReadByte();  // O   79  01001111
      j = s.ReadByte();  // O   79  01001111
      k = s.ReadByte();  // P   80  01010000
      m = s.ReadByte();  // -1  EOF
      n = s.ReadByte();  // -1  EOF
    }

    Console.WriteLine("{0} {1} {2} {3} {4}", i, j, k, m, n);
  }
}

More FileStream Examples
Slide Annotated slide Contents Index
References Textbook 

A program that copies a file

Reference

Program: A FileCopy method in a source file copy-file.cs - uses two FileStreams.
using System;
using System.IO;

public class CopyApp {

  public static void Main(string[] args) {
    FileCopy(args[0], args[1]);
  }
 
  public static void FileCopy(string fromFile, string toFile){
    try{
      using(FileStream fromStream = 
                         new FileStream(fromFile, FileMode.Open)){
        using(FileStream toStream  = 
                         new FileStream(toFile, FileMode.Create)){
          int c;
      
          do{
            c = fromStream.ReadByte();
            if(c != -1) toStream.WriteByte((byte)c);
          } while (c != -1);
        }
      }
    }
    catch(FileNotFoundException e){
      Console.WriteLine("File {0} not found: ", e.FileName);
      throw;
    }
    catch(Exception){
      Console.WriteLine("Other file copy exception");
      throw;
    }
 }

}

Program: A sample activation of the file copy program.
copy-file source-file.txt target-file.txt

Exercise 10.2. A variant of the file copy program

The purpose of this exercise is to train the use of the Read method in class Stream, and subclasses of class Stream.

Write a variant of the file copy program. Your program should copy the entire file into a byte array. Instead of the method ReadByte you should use the Read method, which reads a number of bytes into a byte array. (Please take a careful look at the documentation of Read in class FileStream before you proceed). After this, write out the byte array to standard output such that you can assure yourself that the file is read correctly.

Are you able to read the entire file with a single call to Read? Or do you prefer to read chunks of a certain (maximum) size?

Readers and Writers in C#
Slide Annotated slide Contents Index
References Textbook 

On top of the stream classes, C# offers a number of specific adapter classes that support reading and writing at a higher level of abstraction

Table. An overview of Reader and Writer classes
InputOutput
TextTextReader
    StreamReader
    StringReader
TextWriter
    StreamWriter
    StringWriter
BinaryBinaryReaderBinaryWriter
 

  • Higher level of abstraction

    • IO of chars and text strings - not just raw bytes

    • IO of values in simple types

  • The TypeReader and TypeWriter classes are not subclasses of the stream classes

    • They are typically built on - and delegates to - a Stream object

    • A Reader or Writer classes has a stream - it is not a stream

    • Reader and Writer classes serve as Stream adapters

The class Encoding
Slide Annotated slide Contents Index
References Textbook 

An encoding is a mapping between characters/strings and byte arrays

An object of class System.Text.Encoding represents knowledge about a particular character encoding

Reference

  • byte[] GetBytes(string)       Instance method

  • byte[] GetBytes(char[])       Instance method

    • Encodes a string/char array to a byte array relative to the current encoding

  • char[] GetChars(byte[])       Instance method

    • Decodes a byte array to a char array relative to the current encoding

  • byte[] Convert(Encoding, Encoding, byte[])       Static method

    • Converts a byte array from one encoding (first parameter) to another encoding (second parameter)

Sample use of class Encoding
Slide Annotated slide Contents Index
References Textbook 

Program: Sample encodings, conversions, and decodings of a string of Danish characters.
using System;
using System.Text;

/* Adapted from an example provided by Microsoft */
class ConvertExampleClass{
  public static void Main(){
    string unicodeStr =          // "A æ u å æ ø i æ å"
        "A \u00E6 u \u00E5 \u00E6 \u00F8 i \u00E6 \u00E5";    

    // Different encodings.
    Encoding ascii = Encoding.ASCII,                          
             unicode = Encoding.Unicode,
             utf8 = Encoding.UTF8,
             isoLatin1 = Encoding.GetEncoding("iso-8859-1");  

    // Encodes the characters in a string to a byte array:
    byte[] unicodeBytes = unicode.GetBytes(unicodeStr),       
           asciiBytes =   ascii.GetBytes(unicodeStr),         
           utf8Bytes =   utf8.GetBytes(unicodeStr),
           isoLatin1Bytes =   utf8.GetBytes(unicodeStr);

    // Convert from byte array in unicode to byte array in utf8:
    byte[] utf8BytesFromUnicode =                             
      Encoding.Convert(unicode, utf8, unicodeBytes);          
                                                              
    // Convert from byte array in utf8 to byte array in ascii:
    byte[] asciiBytesFromUtf8 =                               
      Encoding.Convert(utf8, ascii, utf8Bytes);
            
    // Decodes the bytes in byte arrays to a char array:
    char[] utf8Chars = utf8.GetChars(utf8BytesFromUnicode);    
    char[] asciiChars = ascii.GetChars(asciiBytesFromUtf8);    

    // Convert char[] to string:
    string utf8String = new string(utf8Chars),                 
           asciiString = new String(asciiChars);               

    // Display the strings created before and after the conversion.
    Console.WriteLine("Original string: {0}", unicodeStr);     
    Console.WriteLine("String via UTF-8: {0}", utf8String);    
                                                               
    Console.WriteLine("Original string: {0}", unicodeStr);               
    Console.WriteLine("ASCII converted string: {0}", asciiString);       
  }                                                                      
}

Program: Output from the Encoding program.
Original string: A æ u å æ ø i æ å
String via UTF-8: A æ u å æ ø i æ å
Original string: A æ u å æ ø i æ å
ASCII converted string: A ? u ? ? ? i ? ?

  • Conversions in the example

    • From a unicode string to a byte array in a given encoding    
      (GetBytes - Encoding).

    • From a byte array in one encoding to a byte array in another encoding    
      (Convert)

    • From a byte array in a given encoding to a char array (in unicode)
      (GetChars - Decoding).

    • From a char array (in unicode) to a unicode string encoding.    
      (via String constructor)

Exercise 10.3. Finding the encoding of a given text file

Make a UTF-8 text file with some words in Danish. Be sure to use plenty of special Danish characters. You may consider to write a simple C# program to create the file. You may also create the text file in another way.

In this exercise you should avoid writing a byte order mark (BOM) in your UTF-8 text file. (A BOM in the UTF-8 text file may short circuit the decoding we are asking for later in the exercise). One way to avoid the BOM is to denote the UTF-8 encoding with new UTF8Encoding(), or equivalently new UTF8Encoding(false). You may want to consult the constructors in class UFT8Encoding for more information.

Now write a C# program which systematically - in a loop - reads the text file six times with the following objects of type Encoding: ISO-8859-1, UTF-7, UTF-8, UTF-16 (Unicode), UTF32, and 7 bits ASCII.

More concretely, I suggest you make a list of six encoding objects. For each encoding, open a TextReader and read the entire file (with ReadToEnd, for instance) with the current encoding. Echo the characters, which you read, to standard output.

You should be able to recognize the correct, matching encoding (UTF-8) when you see it.

The class TextWriter
Slide Annotated slide Contents Index
References Textbook 

Class TextWriter supports writing of characters and strings via a chosen encoding

It is also possible to write textual representations of simple types with use of TextWriter

  • Subclasses of the abstract TextWriter class

    • StreamWriter: For character output in a particular character encoding

    • StringWriter: For stream access to a string - (discussed later in the lecture)

References

StreamWriter Examples
Slide Annotated slide Contents Index
References Textbook 

The examples illustrate character encoding, and writing values of simple types

Program: Writing a text string using three different encodings with StreamWriters.
using System;
using System.IO;
using System.Text;

public class TextWriterProg{

  public static void Main(){
    string str =      "A æ u å æ ø i æ å",                                   
           strEquiv = "A \u00E6 u \u00E5 \u00E6 \u00F8 i \u00E6 \u00E5";
  
    TextWriter                                                               
      tw1 = new StreamWriter(                         // Iso-Latin-1
             new FileStream("f-iso.txt", FileMode.Create),
             Encoding.GetEncoding("iso-8859-1")),                            

      tw2 = new StreamWriter(                         // UTF-8
             new FileStream("f-utf8.txt", FileMode.Create),
             new UTF8Encoding()),                                            

      tw3 = new StreamWriter(                         // UTF-16              
             new FileStream("f-utf16.txt", FileMode.Create),                 
             new UnicodeEncoding());                                         
                                                                             
    tw1.WriteLine(str);     tw1.WriteLine(strEquiv);                         
    tw2.WriteLine(str);     tw2.WriteLine(strEquiv);                         
    tw3.WriteLine(str);     tw3.WriteLine(strEquiv);

    tw1.Close();                                                             
    tw2.Close();
    tw3.Close();
  }

}

Reference

Program: Writing values of simple types and objects of our own classes.
using System;
using System.IO;

public class TextSimpleTypes{
  
  public static void Main(){
 
    using(TextWriter tw = new StreamWriter("simple-types.txt")){   
      tw.Write(5);  tw.WriteLine();                                
      tw.Write(5.5);  tw.WriteLine();                              
      tw.Write(5555M); tw.WriteLine();
      tw.Write(5==6); tw.WriteLine();
    }

    using(TextWriter twnst = new StreamWriter("non-simple-types.txt")){   
      twnst.Write(new Point(1,2)); twnst.WriteLine();                     
      twnst.Write(new Die(6)); twnst.WriteLine();                         
    }

  }
}

Program: The file simple-types.txt.
5
5,5
5555
False

Program: The file non-simple-types.txt.
Point: (1, 2). 
Die[6]: 3

Exercise 10.4. Die tossing - writing to text file

Write a program that tosses a Die 1000 times, and writes the outcome of the tosses to a textfile. Use a TextWriter to accomplish the task.

Write another program that reads the text file. Report the number of ones, twos, threes, fours, fives, and sixes.

Use a TextWriter subclass, rather than a Stream subclass, to write text files

Members in class StreamWriter
Slide Annotated slide Contents Index
References Textbook 

  • 7 overloaded constructors

    • Parameters involved: File name, stream, encoding, buffer size

    • StreamWriter(String)

    • StreamWriter(Stream)

    • StreamWriter(Stream, Encoding)

    • others

  • 17/18 overloaded Write / WriteLine operations

    • Chars, strings, simple types. Formatted output

  • Encoding

    • A property that gets the encoding used for this TextWriter

  • NewLine

    • A property that gets/sets the applied newline string of this TextWriter

  • others

The class TextReader
Slide Annotated slide Contents Index
References Textbook 

Class TextReader supports reading of characters via a chosen encoding

Class TextReader does not have methods to read textual representation of simple types

  • Subclasses of the abstract TextReader class

    • StreamReader: For character input in a particular character encoding

    • StringReader: For stream access to a string - (discussed later in the lecture)

References

StreamReader Examples
Slide Annotated slide Contents Index
References Textbook 

The examples illustrate encodings and reading values of simple types

Reference

Program: Reading back the text strings encoded in three different ways, with StreamReader. In the last half part of the program, the binary contents of the three files are read and reported.
using System;
using System.IO;
using System.Text;

public class TextReaderProg{

  public static void Main(){
  
    TextReader tr1 = new StreamReader(
                       new FileStream("f-iso.txt", FileMode.Open),
                       Encoding.GetEncoding("iso-8859-1")),
               tr2 = new StreamReader(
                       new FileStream("f-utf8.txt", FileMode.Open),
                       new UTF8Encoding()),
               tr3 = new StreamReader(             // UTF-16
                       new FileStream("f-utf16.txt", FileMode.Open),
                       new UnicodeEncoding());

    Console.WriteLine(tr1.ReadLine());  Console.WriteLine(tr1.ReadLine());  
    Console.WriteLine(tr2.ReadLine());  Console.WriteLine(tr2.ReadLine());  
    Console.WriteLine(tr3.ReadLine());  Console.WriteLine(tr3.ReadLine());  

    tr1.Close();
    tr2.Close();
    tr3.Close();

    // Raw reading of the files to control the contents at byte level
    FileStream  fs1 = new FileStream("f-iso.txt", FileMode.Open),
                fs2 = new FileStream("f-utf8.txt", FileMode.Open),
                fs3 = new FileStream("f-utf16.txt", FileMode.Open);

    StreamReport(fs1, "Iso Latin 1");   
    StreamReport(fs2, "UTF-8");
    StreamReport(fs3, "UTF-16");

    fs1.Close();
    fs2.Close();
    fs3.Close();
  }

  public static void StreamReport(FileStream fs, string encoding){
    Console.WriteLine();
    Console.WriteLine(encoding);
    int ch, i = 0;
    do{
      ch = fs.ReadByte();
      if (ch != -1) Console.Write("{0,4}", ch);
      i++;
      if (i%10 == 0) Console.WriteLine();
    } while (ch != -1);
    Console.WriteLine();
  }

}

Program: Output from the program that reads back the strings encoded in three different ways.
A æ u å æ ø i æ å
A æ u å æ ø i æ å
A æ u å æ ø i æ å
A æ u å æ ø i æ å
A æ u å æ ø i æ å
A æ u å æ ø i æ å

Iso Latin 1
  65  32 230  32 117  32 229  32 230  32
 248  32 105  32 230  32 229  13  10  65
  32 230  32 117  32 229  32 230  32 248
  32 105  32 230  32 229  13  10

UTF-8
  65  32 195 166  32 117  32 195 165  32
 195 166  32 195 184  32 105  32 195 166
  32 195 165  13  10  65  32 195 166  32
 117  32 195 165  32 195 166  32 195 184
  32 105  32 195 166  32 195 165  13  10


UTF-16
 255 254  65   0  32   0 230   0  32   0
 117   0  32   0 229   0  32   0 230   0
  32   0 248   0  32   0 105   0  32   0
 230   0  32   0 229   0  13   0  10   0
  65   0  32   0 230   0  32   0 117   0
  32   0 229   0  32   0 230   0  32   0
 248   0  32   0 105   0  32   0 230   0
  32   0 229   0  13   0  10   0

Reference

Program: A program that reads line of text and parses them to values of simple types.
using System;
using System.IO;

public class TextSimpleTypes{
  
  public static void Main(){
 
    using(TextReader twst = new StreamReader("simple-types.txt")){
      int i = Int32.Parse(twst.ReadLine()); 
      double d = Double.Parse(twst.ReadLine());  
      decimal m = Decimal.Parse(twst.ReadLine());  
      bool b = Boolean.Parse(twst.ReadLine());

      Console.WriteLine("{0} \n{1} \n{2} \n{3}", i, d, m, b);
    }

  }
}

Program: Output from the readline and parsing program.
5 
5,5 
5555 
False

Use a TextReader subclass, rather than a Stream subclass, to read text files

Members in class StreamReader
Slide Annotated slide Contents Index
References Textbook 

  • 10 StreamReader constructors

    • Similar to the StreamWriter constructors

    • StreamReader(String)

    • StreamReader(Stream)

    • StreamReader(Stream, bool)

    • StreamReader(Stream, Encoding)

    • others

  • int Read()     Reads a single character. Returns -1 if at end of file

  • int Read(char[], int, int)     Returns the number of characters read

  • int Peek()

  • String ReadLine()

  • String ReadToEnd()

  • CurrentEncoding

    • A property that gets the encoding of this StreamReader

The class BinaryWriter
Slide Annotated slide Contents Index
References Textbook 

Class BinaryWriter allows us to write values of simple types in binary format

Encodings are only relevant if values of type char are written

References

Program: Use of a BinaryWriter to write some values of simple types.
using System;
using System.IO;

public class BinaryWriteSimpleTypes{
  
  public static void Main(){
    string fn = "simple-types.bin";

    using(BinaryWriter bw = 
            new BinaryWriter(
              new FileStream(fn, FileMode.Create))){
      bw.Write(5);      // 4  bytes
      bw.Write(5.5);    // 8  bytes
      bw.Write(5555M);  // 16 bytes
      bw.Write(5==6);   // 1  bytes
    }

    FileInfo fi = new FileInfo(fn);
    Console.WriteLine("Length of {0}: {1}", fn, fi.Length);

  }
}

Program: Output from the BinaryWriter program - shows size of output file.
Length of simple-types.bin: 29

Reference

Exercise 10.5. Die tossing - writing to a binary file

This exercise is a variant of the die tossing and file writing exercise based on text files.

Modify the program to use a BinaryWriter and a BinaryReader.

Take notice of the different sizes of the text file from the previous exercise and the binary file from this exercise. Explain your observations.

BinaryWriter Members
Slide Annotated slide Contents Index
References Textbook 

  • Two public constructors

    • BinaryWriter(Stream)

    • BinaryWriter(Stream, Encoding)

  • 18 overloaded Write operations

    • One for each simple type

    • Write(char), Write(char[]), and Write(char[], int, int) - use Encoding

    • Write(string) - use Encoding

    • Write(byte[]) and Write(byte[], int, int)

  • Seek(int offset, SeekOrigin origin)

  • others

The class BinaryReader
Slide Annotated slide Contents Index
References Textbook 

Class BinaryReader allows us to read values of simple types in binary format

Symmetric to BinaryWriter

References

Program: Use of a BinaryReader to write the values written by means of the BinaryWriter.
using System;
using System.IO;

public class BinaryReadSimpleTypes{
  
  public static void Main(){
    string fn = "simple-types.bin";

    using(BinaryReader br = 
            new BinaryReader(
              new FileStream(fn, FileMode.Open))){

      int i = br.ReadInt32(); 
      double d = br.ReadDouble();  
      decimal dm = br.ReadDecimal(); 
      bool b = br.ReadBoolean();

      Console.WriteLine("Integer i: {0}", i);
      Console.WriteLine("Double d: {0}", d);
      Console.WriteLine("Decimal dm: {0}", dm);
      Console.WriteLine("Boolean b: {0}", b);
    }

  }
}

Program: Output from the BinaryReader program.
Integer i: 5
Double d: 5,5
Decimal dm: 5555
Boolean b: False

Members in class BinaryReader
Slide Annotated slide Contents Index
References Textbook 

  • Two public constructors

    • BinaryReader(Stream)

    • BinaryReader(Stream, Encoding)

  • 15 individually name Readtype operations

    • ReadBoolean, ReadChar, ReadByte, ReadDouble, ReadDecimal, ReadInt16, ...

  • Three overloaded Read operations

    • Read() and Read (char[] buffer, int index, int count)
      read characters - using Encoding

    • Read (bytes[] buffer, int index, int count) reads bytes

The classes StringReader and StringWriter
Slide Annotated slide Contents Index
References Textbook 

StringReader and StringWriter allow strings to be read and written like streams

A StringWriter is constructed on a StringBuilder object

A StringReader is constructed on a string object

Table. An overview of Reader and Writer classes with special emphasis on StringReader and StringWriter.
InputOutput
TextTextReader
    StreamReader
    StringReader
TextWriter
    StreamWriter
    StringWriter
BinaryBinaryReaderBinaryWriter
 

Program: A StringWriter program similar to the StreamReader program shown earlier.
using System;
using System.IO;
using System.Text;

public class TextSimpleTypes{
  
  public static void Main(){

    StringBuilder sb = new StringBuilder();   // A mutable string
 
    using(TextWriter tw = new StringWriter(sb)){
      for (int i = 0; i < 5; i++){
        tw.Write(5 * i);  tw.WriteLine();
        tw.Write(5.5 * i);  tw.WriteLine();
        tw.Write(5555M * i); tw.WriteLine();
        tw.Write(5 * i == 6); tw.WriteLine();}
    }

    Console.WriteLine(sb);

  }
}

Program: Output of the StringWriter program.
0
0
0
False
5
5,5
5555
False
10
11
11110
False
15
16,5
16665
False
20
22
22220
False

Program: A StringReader program.
using System;
using System.IO;

public class TextSimpleTypes{
  
  public static void Main(){
 
    string str = "5" + "\n" + 
                 "5,5" + "\n" +
                 "5555,0" + "\n" +
                 "false";

    using(TextReader tr = new StringReader(str)){
      int i = Int32.Parse(tr.ReadLine()); 
      double d = Double.Parse(tr.ReadLine());  
      decimal m = Decimal.Parse(tr.ReadLine());  
      bool b = Boolean.Parse(tr.ReadLine());

      Console.WriteLine("{0} \n{1} \n{2} \n{3}", i, d, m, b);
    }

  }
}

Program: Output of the StringReader program.
5 
5,5 
5555,0 
False

The Console class
Slide Annotated slide Contents Index
References Textbook 

The Console class encapsulates the traditional standard input, standard output, and standard error streams

Console.In is a TextReader object.

Console.Out and Console.Error are TextWriter objects

Program: A program that redirects standard output and standard error to a file.
using System;
using System.IO;

class App{

  public static void Main(string[] args){

     TextWriter standardOutput = Console.Out;
     StreamWriter myOut = null,
                  myError = null;

     if (args.Length == 2) {
        Console.Out.WriteLine("Redirecting std output and error to files");
        myOut = new StreamWriter(args[0]);
        Console.SetOut(myOut);
        myError = new StreamWriter(args[1]);
        Console.SetError(myError);
     } else {
        Console.Out.WriteLine("Keeping standard output and error unchanged");
     }

     // Output from this section of the program may be redirected
     Console.Out.WriteLine("Text to std output - by Console.Out.WriteLine");
     Console.WriteLine("Text to standard output -  by Console.WriteLine(...)");
     Console.Error.WriteLine("Error msg - by Console.Error.WriteLine(...)");

     if (args.Length == 2) {
       myOut.Close(); myError.Close();
     }     

     Console.SetOut(standardOutput);
     Console.Out.WriteLine("Now we are back again");
     Console.Out.WriteLine("Good Bye");
  }
}

Program: Output when running the program as App File1 File2.
Redirecting std output and error to files
Now we are back again
Good Bye

Program: File 1 after the execution of the Console demo program.
Text to std output - by Console.Out.WriteLine
Text to standard output -  by Console.WriteLine(...)

Program: File 2 after the execution of the Console demo program.
Error msg - by Console.Error.WriteLine(...)

Program: Output when running the program as just App.
Keeping standard output and error unchanged
Text to std output - by Console.Out.WriteLine
Text to standard output -  by Console.WriteLine(...)
Error msg - by Console.Error.WriteLine(...)
Now we are back again
Good Bye

Members in the Console class
Slide Annotated slide Contents Index
References Textbook 

The Console class offers a rich repertoire of possibilities

All properties and methods in Console are static

  • Access to and control of In, Out, and Error

  • Write, WriteLine, Read, and ReadLine methods

    • Shortcuts to Out.Write, Out.WriteLine, In.Read, and In.ReadLine

  • Many properties and methods that control the underlying buffer and window

    • Size, colors, and positions

  • Immediate, non-blocking input from the Console

    • The property KeyAvailable returns if a key is pressed (non-blocking)

    • ReadKey() returns info about the pressed key (blocking)

  • Other operations

    • Clear(), Beep(), and Beep(int, int) methods.


Directories and Files

The File and FileInfo classes
Slide Annotated slide Contents Index
References Textbook 

The class FileInfo represents a file

The class File holds static methods for creation, copying, deletion, moving, and opening of files

Program: A demonstration of the FileInfo class.
using System;
using System.IO;

public class FileInfoDemo{

  public static void Main(){
    // Setting up file names
    string fileName = "file-info.cs",
           fileNameCopy = "file-info-copy.cs";

    // Testing file existence
    FileInfo fi = new FileInfo(fileName); // this source file
    Console.WriteLine("{0} does {1} exist", 
                      fileName, fi.Exists ? "" : "not");

    // Show file info properties:
    Console.WriteLine("DirectoryName: {0}", fi.DirectoryName);
    Console.WriteLine("FullName: {0}", fi.FullName);
    Console.WriteLine("Extension: {0}", fi.Extension);
    Console.WriteLine("Name: {0}", fi.Name);
    Console.WriteLine("Length: {0}", fi.Length);
    Console.WriteLine("CreationTime: {0}", fi.CreationTime);

    // Copy one file to another
    fi.CopyTo(fileNameCopy);
    FileInfo fiCopy  = new FileInfo(fileNameCopy);

    // Does the copy exist?
    Console.WriteLine("{0} does {1} exist", 
                      fileNameCopy, fiCopy.Exists ? "" : "not"); 

    // Delete the copy again
    fiCopy.Delete();
                                                                 
    // Does the copy exist?                                      
    Console.WriteLine("{0} does {1} exist",                      
                      fileNameCopy, fiCopy.Exists ? "" : "not"); // !!??

    // Create new FileInfo object for the copy
    FileInfo fiCopy1  = new FileInfo(fileNameCopy);
    // Check if the copy exists?
    Console.WriteLine("{0} does {1} exist", fileNameCopy, 
                      fiCopy1.Exists ? "" : "not"); 

    // Achieve a TextReader (StreamReader) from the file info object
    // and echo the first ten lines in the file to standard output  
    using(StreamReader sr = fi.OpenText()){
      for (int i = 1; i <= 10; i++)
        Console.WriteLine("  " + sr.ReadLine());
    }
  }
}

Program: Output from the FileInfo demo program.
file-info.cs does  exist
DirectoryName: c:\users\kurt\courses\prog1-06\sources\c-sharp\io\file-info-demo
FullName: c:\users\kurt\courses\prog1-06\sources\c-sharp\io\file-info-demo\file-info.cs
Extension: .cs
Name: file-info.cs
Length: 1342
CreationTime: 02-11-2006 21:18:31
file-info-copy.cs does  exist
file-info-copy.cs does  exist   
file-info-copy.cs does not exist
  using System;
  using System.IO;
  
  public class FileInfoDemo{
  
    public static void Main(){
      string fileName = "file-info.cs",
             fileNameCopy = "file-info-copy.cs";
  
      FileInfo fi = new FileInfo(fileName); // this source file

Program: A demonstration of the File class.
using System;
using System.IO;

public class FileDemo{

  public static void Main(){

    // Setup file names
    string fileName = "file.cs",             // this source file
           fileNameCopy = "fileCopy.cs";

    // Does this source file exist?
    Console.WriteLine("{0} does {1} exist", 
                      fileName, File.Exists(fileName) ? "" : "not");

    // Copy this source file
    File.Copy(fileName, fileNameCopy);

    // Does the copy exist?
    Console.WriteLine("{0} does {1} exist", 
                      fileNameCopy, 
                     File.Exists(fileNameCopy) ? "" : "not");

    // Delete the copy again
    Console.WriteLine("Deleting {0}", fileNameCopy);
    File.Delete(fileNameCopy);

    // Does the deleted file exist
    Console.WriteLine("{0} does {1} exist", 
                      fileNameCopy, 
                      File.Exists(fileNameCopy) ? "" : "not");

    // Read all lines in source file and echo 
    // one of them to the console
    string[] lines = File.ReadAllLines(fileName);
    Console.WriteLine("Line {0}: {1}", 6, lines[6]);

  }

}

Program: Output from the File demo program.
file.cs does  exist
fileCopy.cs does  exist
Deleting fileCopy.cs
fileCopy.cs does not exist
Line 6:     string fileName = "file.cs",             // this source file

There is a substantial overlap between the instance methods of class FileInfo and the static methods in class File

Members in class FileInfo
Slide Annotated slide Contents Index
References Textbook 

Instance methods and instance properties of class FileInfo

  • A single constructor

    • FileInfo(string)

  • Properties (getters) that access information about the current file

    • Examples: Length, Extension, Directory, Exists, LastAccessTime

  • Stream, reader, and writer factory methods:

    • Examples: Create, AppendText, CreateText, Open, OpenRead, OpenWrite, OpenText

  • Classical file manipulations

    • CopyTo, Delete, MoveTo, Replace

  • Others

    • Refresh, ...

The Directory and DirectoryInfo classes
Slide Annotated slide Contents Index
References Textbook 

The class DirectoryInfo represents a directory

The class Directory holds static methods for creating, moving, and enumerating through directories

Program: A demonstration of the DirectoryInfo class.
using System;
using System.IO;

public class DirectoryInfoDemo{

  public static void Main(){
    string fileName = "directory-info.cs";    // The current source file

    // Get the DirectoryInfo of the current directory
    // from the FileInfo of the current source file 
    FileInfo fi = new FileInfo(fileName);     // This source file
    DirectoryInfo di = fi.Directory;

    Console.WriteLine("File {0} is in directory \n   {1}", fi, di);

    // Get the files and directories in the parent directory.
    FileInfo[] files = di.Parent.GetFiles();
    DirectoryInfo[] dirs = di.Parent.GetDirectories();

    // Show the name of files and directories on the console
    Console.WriteLine("\nListing directory {0}:", di.Parent.Name);
    foreach(DirectoryInfo d in dirs)
      Console.WriteLine(d.Name);
    foreach(FileInfo f in files)
      Console.WriteLine(f.Name);

  }
}

Program: Output from the DirectoryInfo demo program.
File directory-info.cs is in directory 
   C:\users\kurt\courses\prog1-06\sources\c-sharp\io\directory-info-demo

Listing directory io:
console
decorator
directory-info-demo
encoding
file-copy
file-demo
file-info-demo
filestream-ex
guess-encoding
text-writer-reader
README

Program: A demonstration of the Directory class - similar to the DirectoryInfo demo program.
using System;
using System.IO;

public class DirectoryDemo{

  public static void Main(){
    string fileName = "directory.cs";       // The current source file
    FileInfo fi = new FileInfo(fileName);   // This source file

    string thisFile =  fi.FullName,
           thisDir =   Directory.GetParent(thisFile).FullName,
           parentDir = Directory.GetParent(thisDir).FullName;

    Console.WriteLine("This file: {0}", thisFile);
    Console.WriteLine("This Directory: {0}", thisDir);
    Console.WriteLine("Parent directory: {0}", parentDir);

    string[] files = Directory.GetFiles(parentDir);
    string[] dirs =  Directory.GetDirectories(parentDir);

    Console.WriteLine("\nListing directory {0}:", parentDir);
    foreach(string d in dirs)
      Console.WriteLine(d);
    foreach(string f in files)
      Console.WriteLine(f);

  }
}

Program: Output from the Directory demo program.
This file: C:\users\kurt\courses\prog1-06\sources\c-sharp\io\directory-demo\directory.cs
This Directory: C:\users\kurt\courses\prog1-06\sources\c-sharp\io\directory-demo
Parent directory: C:\users\kurt\courses\prog1-06\sources\c-sharp\io

Listing directory C:\users\kurt\courses\prog1-06\sources\c-sharp\io:
C:\users\kurt\courses\prog1-06\sources\c-sharp\io\console
C:\users\kurt\courses\prog1-06\sources\c-sharp\io\decorator
C:\users\kurt\courses\prog1-06\sources\c-sharp\io\directory-demo
C:\users\kurt\courses\prog1-06\sources\c-sharp\io\directory-info-demo
C:\users\kurt\courses\prog1-06\sources\c-sharp\io\encoding
C:\users\kurt\courses\prog1-06\sources\c-sharp\io\file-copy
C:\users\kurt\courses\prog1-06\sources\c-sharp\io\file-demo
C:\users\kurt\courses\prog1-06\sources\c-sharp\io\file-info-demo
C:\users\kurt\courses\prog1-06\sources\c-sharp\io\filestream-ex
C:\users\kurt\courses\prog1-06\sources\c-sharp\io\guess-encoding
C:\users\kurt\courses\prog1-06\sources\c-sharp\io\text-writer-reader
C:\users\kurt\courses\prog1-06\sources\c-sharp\io\README

As for File and FileInfo, there is substantial overlap between the classes Directory and DirectoryInfo

Members in class DirectoryInfo
Slide Annotated slide Contents Index
References Textbook 

Instance properties and instance methods of class DirectoryInfo

  • A single constructor

    • DirectoryInfo(string)

  • Properties (getters) that access information about the current directory

    • Examples: CreationTime, LastAccessTime, Exists, Name, FullName

  • Directory Navigation operations

    • Up: Parent, Root

    • Down: GetDirectories, GetFiles, GetFileSystemInfo (all overloaded)

  • Classical directory manipulations

    • Create, MoveTo, Delete

  • Others

    • Refresh, ...

The class DirectoryInfo and FileInfo have a common, abstract superclass called FileSystemInfo


Serialization

Serialization
Slide Annotated slide Contents Index
References Textbook 

Serialization provides for input and output of a network of objects

  • Serialization

    • Writes an object o to a file

    • Also writes the objects referred from o

  • Deserialization

    • Reads a serialized file in order to reestablish the serialized object o

    • Also reestablishes the network of objects originally referred from o

  • Serialization and deserialization is supported via classes that implement the Iformatter interface:

    • BinaryFormatter and SoapFormatter

  • Methods in Iformatter:

    • Serialize and Deserialize

Examples of Serialization in C#
Slide Annotated slide Contents Index
References Textbook 

Straightforward serialization of Person and Date objects

Program: The Person class - Serializable.
using System;

[Serializable]
public class Person{

  private string name;
  private int age;    // Redundant
  private Date dateOfBirth, dateOfDeath;

  public Person (string name, Date dateOfBirth){
    this.name = name;
    this.dateOfBirth = dateOfBirth;
    this.dateOfDeath = null;
    age = Date.Today.YearDiff(dateOfBirth);
  }

  public Date DateOfBirth {
    get {return new Date(dateOfBirth);}
  }

  public int Age{
    get {return Alive ? age : dateOfDeath.YearDiff(dateOfBirth);}
  }

  public bool Alive{
    get {return dateOfDeath == null;}
  }

  public void Died(Date d){
    dateOfDeath = d;
  }

  public void Update(){
    age = Date.Today.YearDiff(dateOfBirth);
  }

  public override string ToString(){
    return "Person: " + name + 
            "  *" + dateOfBirth + 
            (Alive ? "" : "  +" + dateOfDeath) +
            "  Age: " + age;
  }

}

Program: The Date class - Serializable.
using System;

[Serializable]
public class Date{
  private ushort year;
  private byte month, day;
  private DayOfWeek nameOfDay;    // Redundant

  public Date(int year, int month, int day){
    this.year =  (ushort)year; 
    this.month = (byte)month; 
    this.day =   (byte)day;
    this.nameOfDay = (new DateTime(year, month, day)).DayOfWeek;
  }

  public Date(Date d){
    this.year = d.year; this.month = d.month; 
    this.day = d.day; this.nameOfDay = d.nameOfDay;
  }

  public int Year{get{return year;}}
  public int Month{get{return month;}}
  public int Day{get{return day;}}

  // return this minus other, as of usual birthday calculations.
  public int YearDiff(Date other){
    if (this.Equals(other))
       return 0;
    else if ((new Date(other.year, this.month, this.day)).IsBefore(other))
      return this.year - other.year - 1; 
    else
      return this.year - other.year;
  }

  public override bool Equals(Object obj){
     if (obj == null)
       return false;
     else if (this.GetType() != obj.GetType())
       return false;
     else if (ReferenceEquals(this, obj))
       return true;
     else if (this.year == ((Date)obj).year &&
              this.month == ((Date)obj).month &&
              this.day == ((Date)obj).day)
       return true;
     else return false;
   }

  // Is this date less than other date
  public bool IsBefore(Date other){
    return 
      this.year < other.year ||
      this.year == other.year && this.month < other.month ||
      this.year == other.year && this.month == other.month && this.day < other.day;
  }


  public static Date Today{
    get{
      DateTime now = DateTime.Now;
      return new Date(now.Year, now.Month, now.Day);}
  }

  public override string ToString(){
    return string.Format("{0} {1}.{2}.{3}", nameOfDay, day, month, year);
  }
}

Program: The Person client class - applies serialization and deserialization.
using System;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;

class Client{

  public static void Main(){
    Person p = new Person("Peter", new Date(1936, 5, 11));
    p.Died(new Date(2007,5,10));
    Console.WriteLine("{0}", p);

    using (FileStream strm = 
               new FileStream("person.dat", FileMode.Create)){
      IFormatter fmt = new BinaryFormatter();
      fmt.Serialize(strm, p);
    }

    // -----------------------------------------------------------
    p = null;
    Console.WriteLine("Reseting person");
    // -----------------------------------------------------------
    
    using (FileStream strm = 
               new FileStream("person.dat", FileMode.Open)){
      IFormatter fmt = new BinaryFormatter();
      p = fmt.Deserialize(strm) as Person;
    }

    Console.WriteLine("{0}", p);
  }

}

Program: Output of the Person client class.
Person: Peter  *Monday 11.5.1936  +Thursday 10.5.2007  Age: 71
Reseting person
Person: Peter  *Monday 11.5.1936  +Thursday 10.5.2007  Age: 71

Exercise 10.7. Serializing one of your own classes

The purpose of this exercise is to let you have your first experiences with serialization of some of your own object, which are instances of your own classes.

Select one or more of your own classes, typically from the project on which you are working.

Mark the classes as Serializable, in the same way as we have done in class Person and class Date on the accompanying slide page.

Consider if some of the instance variables should be marked as NonSerialized and if an OnDeserialized method should be provided.

Like in the client program of class Person and class Date, make a sample client class of your own classes, and call the Serialize and the Deserialize methods.

Exercise 10.7. Serializing with an XML formatter

In the programs shown on the accompanying slide we have used a binary formatter for serialization of Person and Date object.

Modify the client program to use a so-called Soap formatter in the namespace System.Runtime.Serialization.Formatters.Soap. SOAP is an XML language intended for exchange of XML documents. SOAP is related to the discipline of web services in the area of Internet technology.

After the serialization you should take a look at the file person.dat, which is written and read by the client program.

Custom Serialization
Slide Annotated slide Contents Index
References Textbook 

The programmer can control the serialization and deserialization

In class Person and Date certain fields are not serialized

Program: The Person class - Serialization control with attributes.
using System;
using System.Runtime.Serialization;

[Serializable]
public class Person{

  private string name;

  [NonSerialized()]
  private int age;    

  private Date dateOfBirth, dateOfDeath;

  public Person (string name, Date dateOfBirth){
    this.name = name;
    this.dateOfBirth = dateOfBirth;
    this.dateOfDeath = null;
    age = Date.Today.YearDiff(dateOfBirth);
  }

  [OnDeserialized()] 
  internal void FixPersonAfterDeserializing(
                           StreamingContext context){
    age = Date.Today.YearDiff(dateOfBirth);
  }

  public Date GetDateOfBirth(){
    return new Date(dateOfBirth);
  }

  public int Age{
    get {return Alive ? age : dateOfDeath.YearDiff(dateOfBirth);}
  }

  public bool Alive{
    get {return dateOfDeath == null;}
  }

  public void Died(Date d){
    dateOfDeath = d;
  }

  public void Update(){
    age = Date.Today.YearDiff(dateOfBirth);
  }

  public override string ToString(){
    return "Person: " + name + 
            "  *" + dateOfBirth + 
            (Alive ? "" : "  +" + dateOfDeath) +
            "  Age: " + age;
  }

}

Program: The Date class - Serialization control with attributes .
using System;
using System.Runtime.Serialization;

[Serializable]
public class Date{
  private ushort year;
  private byte month, day;

  [NonSerialized()]
  private DayOfWeek nameOfDay;

  public Date(int year, int month, int day){
    this.year =  (ushort)year; 
    this.month = (byte)month; 
    this.day =   (byte)day;
    this.nameOfDay = (new DateTime(year, month, day)).DayOfWeek;
  }

  public Date(Date d){
    this.year = d.year; this.month = d.month; 
    this.day = d.day; this.nameOfDay = d.nameOfDay;
  }

  [OnDeserialized()]
  internal void FixDateAfterDeserializing(
                                   StreamingContext context){
    nameOfDay = (new DateTime(year, month, day)).DayOfWeek;    
  }

  public int Year{get{return year;}}
  public int Month{get{return month;}}
  public int Day{get{return day;}}

  // return this minus other, as of usual birthday calculations.
  public int YearDiff(Date other){
    if (this.Equals(other))
       return 0;
    else if ((new Date(other.year, this.month, this.day)).IsBefore(other))
      return this.year - other.year - 1; 
    else
      return this.year - other.year;
  }

  public override bool Equals(Object obj){
     if (obj == null)
       return false;
     else if (this.GetType() != obj.GetType())
       return false;
     else if (ReferenceEquals(this, obj))
       return true;
     else if (this.year == ((Date)obj).year &&
              this.month == ((Date)obj).month &&
              this.day == ((Date)obj).day)
       return true;
     else return false;
   }

  // Is this date less than other date
  public bool IsBefore(Date other){
    return 
      this.year < other.year ||
      this.year == other.year && this.month < other.month ||
      this.year == other.year && this.month == other.month && this.day < other.day;
  }


  public static Date Today{
    get{
      DateTime now = DateTime.Now;
      return new Date(now.Year, now.Month, now.Day);}
  }

  public override string ToString(){
    return string.Format("{0} {1}.{2}.{3}", nameOfDay, day, month, year);
  }
}

Program: The Person client class - applies serialization. Unchanged.
using System;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;

class Client{

  public static void Main(){
    Person p = new Person("Peter", new Date(1936, 5, 11));
    p.Died(new Date(2007,5,10));
    Console.WriteLine("{0}", p);

    using (FileStream strm = 
              new FileStream("person.dat", FileMode.Create)){
      IFormatter fmt = new BinaryFormatter();
      fmt.Serialize(strm, p);
    }

    // -----------------------------------------------------------
    p = null;
    Console.WriteLine("Reseting person");
    // -----------------------------------------------------------
    
    using (FileStream strm = 
              new FileStream("person.dat", FileMode.Open)){
      IFormatter fmt = new BinaryFormatter();
      p = fmt.Deserialize(strm) as Person;
    }

    Console.WriteLine("{0}", p);
  }

}

Program: Output of the Person client class.
Person: Peter  *Monday 11.5.1936  +Thursday 10.5.2007  Age: 71
Reseting person
Person: Peter  *Monday 11.5.1936  +Thursday 10.5.2007  Age: 71

Considerations about Serialization
Slide Annotated slide Contents Index
References Textbook 

  • Security

    • Encapsulated and private data is made available in files

  • Versioning

    • The private state of class C is changed

    • It may not be possible to read serialized objects of type C

Serialization and Alternatives
Slide Annotated slide Contents Index
References Textbook 

When is it appropriate to use serialization?

What are the alternatives to serialization?

  • Serialization

    • An easy way to save and restore objects in between program sessions

    • Useful in many projects where persistency is necessary, but not a key topic

    • Requires only little programming

  • Custom programmed file IO

    • Full control of object IO

    • May require a lot of programming

  • Objects in Relational Databases

    • Impedance mismatch: "Circular objects in retangular boxes"

    • Useful when the program handles large amounts of data

    • Useful if the data is accessed simultaneous from several programs

    • Not a topic in this course

Attributes
Slide Annotated slide Contents Index
References Textbook 

Attributes offer a mechanism that allows the programmer to extend the programming language in simple ways.

Attributes can be accessed by the compiler and - at run-time - by the interpreter

Program: An obsolete class C, and a class D with an obsolete method M.
using System;

[Obsolete("Use class D instead")]
class C{
  // ...
}

class D{
  [Obsolete("Do not call this method.",true)]
  public void M(){
  }
}

class E{
  public static void Main(){
    C c = new C();
    D d = new D();
    d.M();
  }
}

Program: Compiling class C, D, and E.
>csc prog.cs
Microsoft (R) Visual C# 2005 Compiler version 8.00.50727.42
for Microsoft (R) Windows (R) 2005 Framework version 2.0.50727
Copyright (C) Microsoft Corporation 2001-2005. All rights reserved.

prog.cs(16,5): warning CS0618: 'C' is obsolete: 'Use class D instead'
prog.cs(16,15): warning CS0618: 'C' is obsolete: 'Use class D instead'
prog.cs(18,5): error CS0619: 'D.M()' is obsolete: 'Do not call this method.'

  • The attribute Obsolete is defined by a predefined class ObsoleteAttribute

    • A subclass of System.Attribute

    • Positional attribute parameters correspond to constructor parameters

    • Named attribute parameters specify assignment to properties

Program: A reproduction of class ObsoleteAttribute.
// In part, reproduced from the book "C# to the Point"

using System;

[AttributeUsage(AttributeTargets.Method | 
                AttributeTargets.Property)]
public sealed class MyObsoleteAttribute: Attribute{
  string message;
  bool isError;

  public string Message{
    get {
      return message;
    }
  }

  public bool IsError{
    get {
      return isError;
    }
    set {
      isError = value;
    }
  }

  public MyObsoleteAttribute(){
    message = ""; isError = false;
  }

  public MyObsoleteAttribute(string msg){
    message = msg; isError = false;
  }

  public MyObsoleteAttribute(string msg, bool error){
    message = msg; isError = error;
  }

}

Program: Sample usage of the reproduced class - causes a compilation error.
using System;

[MyObsolete("Use class D instead")]
class C{
  // ...
}

class D{
  [MyObsolete("Do not call this method.",IsError=true)]
  public void M(){
  }
}

class E{
  public static void Main(){
    C c = new C();
    D d = new D();
    d.M();
  }
}


Patterns and Techniques

The Decorator Pattern
Slide Annotated slide Contents Index
References Textbook 

The Decorator design pattern attaches responsibilities to objects dynamically

Reference

A template of the class structure in the Decorator design pattern.

  • Component: Defines the common interface of participants in the Decorator pattern

  • Decorator: References another Component to which it delegates responsibilities

The Decorator Pattern at Run Time
Slide Annotated slide Contents Index
References Textbook 

Two decorator objects of a ConcreteComponent object

  • Decorators can be added at run-time

  • A client accesses the outer Component (typically a ConcreteDecorator).

  • A decorator does part of the work, and delegates the rest to the next Component in the chain

Use of Decorator can be seen as a dynamic alternative to static subclassing

The Decorator Pattern and Streams
Slide Annotated slide Contents Index
References Textbook 

We build a compressed stream on a buffered stream on a file stream

The compressed stream decorates the buffered stream

The buffered stream decorates the file stream

Compression and buffering decoration of a FileStream

Program: A program that compresses a file.
using System;
using System.IO;
using System.IO.Compression;

public class CompressProg{

  public static void Main(string[] args){
    byte[] buffer;
    long originalLength;
  
    // Read a file, arg[0], into buffer
    using(Stream infile = new FileStream(args[0], FileMode.Open)){
       buffer = new byte[infile.Length];     
       infile.Read(buffer, 0, buffer.Length);
       originalLength = infile.Length;
    }

    // Compress buffer to a GZipStream
    Stream compressedzipStream = 
       new GZipStream(
          new BufferedStream( 
                  new FileStream(
                        args[1], FileMode.Create),
                  128),
          CompressionMode.Compress);
    compressedzipStream.Write(buffer, 0, buffer.Length);
    compressedzipStream.Close();

    // Report compression rate:
    Console.WriteLine("CompressionRate: {0}/{1}", 
                       MeasureFileLength(args[1]),
                       originalLength);

  }

  public static long MeasureFileLength(string fileName){
    using(Stream infile = new FileStream(fileName, FileMode.Open))
      return infile.Length;
  }

}

Program: Sample application together with program output (compression rate).
> compress compress.cs out
CompressionRate: 545/1126

Program: The corresponding program that decompresses the file.
using System;
using System.IO;
using System.IO.Compression;

public class CompressProg{

  public static void Main(string[] args){
    byte[] buffer;
    const int LargeEnough = 10000;
  
    Stream compressedzipStream = 
       new GZipStream( 
         new BufferedStream(
                new FileStream(
                      args[0], FileMode.Open),
                128),
         CompressionMode.Decompress);

    buffer = new byte[LargeEnough];
    
    // Read and decompress the compressed stream:
    int bytesRead = 0,
        bufferPtr = 0;
    do{
      // Read chunks of 10 bytes per call of Read:
      bytesRead = compressedzipStream.Read(buffer, bufferPtr, 10); 
      if (bytesRead != 0) bufferPtr += bytesRead;
    } while (bytesRead != 0);

    compressedzipStream.Close();

    // Write contens of buffer to the output file
    using(Stream outfile = new FileStream(args[1], FileMode.Create)){
       outfile.Write(buffer, 0, bufferPtr);
    }
  }

}


Collected references
Contents Index
The class System.IO.Stream MSDN2 API Documentation
The class System.IO.FileStream MSDN2 API Documentation
Interfaces in from the C# library Earlier in these notes
A similar program used to illustrate possible errors Earlier in these notes
The class System.Text.Encoding MSDN2 API Documentation
StringWriter Later in these notes
The class System.IO.StringWriter MSDN2 API Documentation
The class System.IO.StreamWriter MSDN2 API Documentation
The class System.IO.TextWriter MSDN2 API Documentation
The similar reading program Later in these notes
StringReader Later in these notes
The class System.IO.StringReader MSDN2 API Documentation
The class System.IO.StreamReader MSDN2 API Documentation
The class System.IO.TextReader MSDN2 API Documentation
Overview Earlier in these notes
The similar writing program Earlier in these notes
The class System.IO.BinaryWriter MSDN2 API Documentation
The similar reading program Later in these notes
The similar writing program Earlier in these notes
The class System.IO.BinaryReader MSDN2 API Documentation
The Composite design pattern Earlier in these notes

 

Chapter 10: Input and Output Classes
Course home     Author home     About producing this web     Previous lecture (top)     Next lecture (top)     Previous lecture (bund)     Next lecture (bund)     
Generated: February 7, 2011, 12:19:26