Unit 5: Understanding .NET Assemblies & Libaries · Unit 5: Understanding .NET Assemblies &...
Transcript of Unit 5: Understanding .NET Assemblies & Libaries · Unit 5: Understanding .NET Assemblies &...
Unit 5: Understanding .NET Assemblies & Libraries 1
Prof. Sushant S Sundikar C# .Net Programming
Unit V: Understanding .NET Assemblies & Libraries
• The Role of .NET Assemblies,
o Understanding the format of .NET Assemblies
o Building and Consuming a Single- File Assembly
o Building and Consuming a Multi-File Assembly
o Shared Assemblies
• The System IO Namespace
o Directory (Info) and File (Info) types,
o Working with DirectoryInfo, Directory, FileInfo, File Classes
• Abstract Stream Class,
o StreamWriters and StreamReaders
o StringWriters and StringReaders
o BinaryWriters and BinaryReaders
o Programmatically watching files
.NET applications are constructed by piecing together any number of assemblies. Simply put, an
assembly is a versioned, self-describing binary file hosted by the CLR. .NET assemblies have *.exe or
*.dll file extensions. Here are some of the benefits provided by the assembly.
Assemblies Promote Code Reuse
A code library (also termed a class library) is a *.dll that contains types (classes, interfaces,
namespaces) intended to be used by external applications. The .NET platform allows you to reuse
types in a language-independent manner. For example, you could create a code library in C# and
reuse that library in any other .NET programming language. It is possible to not only allocate types
(classes, interfaces, and namespaces) across languages, but derive from them as well. A base class
defined in C# could be extended by a class authored in Visual Basic .NET. The point is that when you
begin to break apart a single executable file into numerous .NET assemblies, you achieve a language-
neutral form of code reuse.
Assemblies Establish a Type Boundary
The assembly in which a type resides establishes a type’s identity. For example, if you have two
uniquely named assemblies (say, MyCars.dll and YourCars.dll) that both define a namespace
(CarLibrary) containing a class named SportsCar, they are considered unique types in the .NET
universe.
Assemblies Are Versionable Units
.NET assemblies are assigned a four-part numerical version number of the form
<major>.<minor>.<build>.<revision> (if you do not explicitly provide a version number using the
[AssemblyVersion] attribute, the assembly is automatically assigned a version of 0.0.0.0). This
number, in conjunction with an optional public key value, allows multiple versions of the same
assembly to coexist in harmony on a single machine.
Unit 5: Understanding .NET Assemblies & Libraries 2
Prof. Sushant S Sundikar C# .Net Programming
Assemblies Are Self-Describing
Assemblies are regarded as self-describing in part because they record every external assembly it
must have access to in order to function correctly. Thus, if your assembly requires
System.Windows.Forms.dll and System.Drawing.dll, they will be documented in the assembly’s
manifest.
In addition to manifest data, an assembly contains metadata that describes the composition
(member names, implemented interfaces, base classes, constructors and so forth) of every
contained type. Given that an assembly is documented in such vivid detail, the CLR does not consult
the Win32 system registry to resolve its location.
Assemblies Are Configurable
Assemblies can be deployed as “private” or “shared.” Private assemblies reside in the same directory
(or possibly a subdirectory) as the client application making use of them. Shared assemblies, on the
other hand, are libraries intended to be consumed by numerous applications on a single machine
and are deployed to a specific directory termed the Global Assembly Cache (GAC).
Regardless of how you deploy your assemblies, you are free to author XML-based configuration files.
Using these configuration files, the CLR can be instructed to “probe” for assemblies under a specific
location, load a specific version of a referenced assembly for a particular client, or consult an
arbitrary directory on your local machine, your network location, or a web-based URL.
Understanding the Format of a .NET Assembly
A .NET assembly (*.dll or *.exe) consists of the following elements:
• A Win32 file header
• A CLR file header
• CIL code
• Type metadata
• An assembly manifest
• Optional embedded resources
The Win32 File Header
The Win32 file header establishes the fact that the assembly can be loaded and manipulated by the
Windows family of operating systems. This header data also identifies the kind of application
(consolebased, GUI-based, or *.dll code library) to be hosted by the Windows operating system.
The CLR File Header
The CLR header is a block of data that all .NET files must support in order to be hosted by the CLR. In
a nutshell, this header defines numerous flags that enable the runtime to understand the layout of
the managed file. For example, flags exist that identify the location of the metadata and resources
within the file, the version of the runtime the assembly was built against, the value of the (optional)
public key, and so forth.
Unit 5: Understanding .NET Assemblies & Libraries 3
Prof. Sushant S Sundikar C# .Net Programming
CIL Code, Type Metadata, and the Assembly Manifest
At its core, an assembly contains CIL code, which as you recall is a platform- and CPU-agnostic
intermediate language. At runtime, the internal CIL is compiled on the fly (using a just-in-time [JIT]
compiler) to platform- and CPU-specific instructions.
An assembly also contains metadata that completely describes the format of the contained types as
well as the format of external types referenced by this assembly. The .NET runtime uses this
metadata to resolve the location of types (and their members) within the binary, lay out types in
memory, and facilitate remote method invocations.
An assembly must also contain an associated manifest (also referred to as assembly metadata).The
manifest documents each module within the assembly, establishes the version of the assembly, and
also documents any external assemblies referenced by the current assembly.
Optional Assembly Resources
Finally, a .NET assembly may contain any number of embedded resources such as application icons,
image files, sound clips, or string tables.
Single-File and Multifile Assemblies
Technically speaking, an assembly can be composed of multiple modules. A module is really nothing
more than a generic term for a valid .NET binary file. In most situations, an assembly is in fact
composed of a single module. In this case, there is a one-to-one correspondence between the
(logical) assembly and the underlying (physical) binary (hence the term single-file assembly).
Single-file assemblies contain all of the necessary elements (header information, CIL code, type
metadata, manifest, and required resources) in a single *.exe or *.dll package. Figure 5-1 illustrates
the composition of a single-file assembly.
Figure 5-1: A single-file assembly
Unit 5: Understanding .NET Assemblies & Libraries 4
Prof. Sushant S Sundikar C# .Net Programming
A multifile assembly, on the other hand, is a set of .NET *.dlls that are deployed and versionedas a
single logic unit. Formally speaking, one of these *.dlls is termed the primary module and contains
the assembly-level manifest (as well as any necessary CIL code, metadata, header information, and
optional resources). The manifest of the primary module records each of the related *.dll files it is
dependent upon.
As a naming convention, the secondary modules in a multifile assembly take a *.netmodule file
extension; however, this is not a requirement of the CLR. Secondary *.netmodules also contain CIL
code and type metadata, as well as a module-level manifest, which simply records the externally
required assemblies of that specific module.
The major benefit of constructing multifile assemblies is that they provide a very efficient way to
download content. For example, assume you have a machine that is referencing a remote multifile
assembly composed of three modules, where the primary module is installed on the client. If the
client requires a type within a secondary remote *.netmodule, the CLR will download the binary to
the local machine on demand to a specific location termed the download cache.
Another benefit of multifile assemblies is that they enable modules to be authored using multiple
.NET programming languages Once each of the individual modules has been compiled, the modules
can be logically “connected” into a logical assembly using tools such as the assembly linker (al.exe).
Figure 11-4 illustrates a multifile assembly composed of three modules, each authored using a
unique .NET programming language.
Figure 5-2. The primary module records secondary modules in the assembly manifest
Unit 5: Understanding .NET Assemblies & Libraries 5
Prof. Sushant S Sundikar C# .Net Programming
Exploring the System.IO Namespace
The System.IO namespace is the region of the base class libraries devoted to file-based (and
memory-based) input and output (I/O) services. Like any namespace, System.IO defines a set of
classes, interfaces, enumerations, structures, and delegates, which focus on the programmatic
manipulation of physical directories and files.
Key Members of the System.IO Namespace
Nonabstract I/O Class Type Meaning in Life
BinaryReader These types allow you to store and retrieve primitive data types
BinaryWriter (integers, Booleans, strings, and whatnot) as a binary value.
BufferedStream This type provides temporary storage for a stream of bytes that may be
committed to storage at a later time.
Directory These types are used to manipulate a machine’s directory
DirectoryInfo structure. The Directory type exposes functionality primarily as static methods. The
DirectoryInfo type exposes similar functionality from a valid object
variable.
DriveInfo This type (new to .NET 2.0) provides detailed information regarding the
drives on a given machine.
File
FileInfo
These types are used to manipulate a machine’s set of files. The File type
exposes functionality primarily as static methods. The FileInfo type
exposes similar functionality from a valid object variable.
FileStream This type allows for random file access (e.g., seeking capabilities) with
data represented as a stream of bytes.
FileSystemWatcher This type allows you to monitor the modification of a given external file.
MemoryStream This type provides random access to streamed data stored in memory
rather than a physical file.
Path This type performs operations on System.String types that contain file or
directory path information in a platform-neutral manner.
StreamWriter
StreamReader
These types are used to store (and retrieve) textual information to (or
from) a file. These types do not support random file access.
StringWriter
StringReader
Like the StreamReader/StreamWriter types, these classes also work with
textual information. However, the underlying storage is a string buffer
rather than a physical file.
Unit 5: Understanding .NET Assemblies & Libraries 6
Prof. Sushant S Sundikar C# .Net Programming
The Directory(Info) and File(Info) Types
System.IO provides four classes that allow you to manipulate individual files, as well as interact with
a machine’s directory structure. The first two classes, Directory and File, expose creation, deletion,
copying, and moving operations using various static members. The closely related FileInfo and
DirectoryInfo classes expose similar functionality as instance-level methods. In Figure 5-1, notice
that the Directory and File types directly extend System.Object, while DirectoryInfo and FileInfo
derive from the abstract FileSystemInfo type.
Figure 5-1 : The File- and Directory-centric types
The Abstract FileSystemInfo Base Class
The DirectoryInfo and FileInfo types receive many behaviors from the abstract FileSystemInfo base
class.
Table 5-2. FileSystemInfo Properties
Property Meaning in Life
Attributes Gets or sets the attributes associated with the current file that are
represented by the FileAttributes enumeration.
CreationTime Gets or sets the time of creation for the current file or directory.
Exists Can be used to determine if a given file or directory exists.
Extension Retrieves a file’s extension.
FullName Gets the full path of the directory or file.
LastAccessTime Gets or sets the time the current file or directory was last accessed.
LastWriteTime Gets or sets the time when the current file or directory was last written
to.
Name For files, gets the name of the file. For directories, gets the name of the
last directory in the hierarchy if a hierarchy exists. Otherwise, the Name
Unit 5: Understanding .NET Assemblies & Libraries 7
Prof. Sushant S Sundikar C# .Net Programming
property gets the name of the directory.
Working with the DirectoryInfo Type
DirectoryInfo class contains a set of members used for creating, moving, deleting, and enumerating
over directories and subdirectories. In addition to the functionality provided by its base class
(FileSystemInfo), DirectoryInfo offers the key members in Table 5-3.
Table 16-3. Key Members of the DirectoryInfo Type
Members Meaning in Life
Create()
CreateSubdirectory()
Create a directory (or set of subdirectories), given a path name
Delete() Deletes a directory and all its contents
GetDirectories() Returns an array of strings that represent all subdirectories in the current
directory
GetFiles() Retrieves an array of FileInfo types that represent a set of files in the given
directory
MoveTo() Moves a directory and its contents to a new path
Parent Retrieves the parent directory of the specified path
Root Gets the root portion of a path
You begin working with the DirectoryInfo type by specifying a particular directory path as a
constructor parameter. If you want to obtain access to the current application directory (i.e., the
directory of the executing application), use the "." notation. Here are some examples:
// Bind to the current application directory.
DirectoryInfo dir1 = new DirectoryInfo(".");
// Bind to C:\Windows,using a verbatim string.
DirectoryInfo dir2 = new DirectoryInfo(@"C:\Windows");
In the second example, you are making the assumption that the path passed into the constructor
(C:\Windows) already exists on the physical machine. However, if you attempt to interact with a
nonexistent directory, a System.IO.DirectoryNotFoundException is thrown. Thus, if you specify a
directory that is not yet created, you will need to call the Create() method before proceeding:
// Bind to a nonexistent directory, then create it.
DirectoryInfo dir3 = new DirectoryInfo(@"C:\Windows\Testing");
dir3.Create();
Program ch05pg05.cs : Program to demonstrate the use of DirectoryInfo Class
Unit 5: Understanding .NET Assemblies & Libraries 8
Prof. Sushant S Sundikar C# .Net Programming
class ch05pg05
{
static void Main(string[] args)
{
Console.WriteLine("***** Fun with Directory(Info) *****\n");
DirectoryInfo dir = new DirectoryInfo(@"C:\Windows");
// Dump directory information.
Console.WriteLine("***** Directory Info *****");
Console.WriteLine("FullName: {0} ", dir.FullName);
Console.WriteLine("Name: {0} ", dir.Name);
Console.WriteLine("Parent: {0} ", dir.Parent);
Console.WriteLine("Creation: {0} ", dir.CreationTime);
Console.WriteLine("Attributes: {0} ", dir.Attributes);
Console.WriteLine("Root: {0} ", dir.Root);
Console.WriteLine("**************************\n");
}
}
The FileAttributes Enumeration
The Attributes property exposed by FileSystemInfo provides various traits for the current directory
or file, all of which are represented by the FileAttributes enumeration (enum).
public enum FileAttributes
{
ReadOnly, Hidden,
// The file is part of the operating system or is used exclusively
by the operating system
System, Directory, Archive,
// This name is reserved for future use.
Device,
// The file is 'normal' as it has no other attributes set.
Normal, Temporary,
// Sparse files are typically large files whose data are mostly
zeros.
SparseFile,
Unit 5: Understanding .NET Assemblies & Libraries 9
Prof. Sushant S Sundikar C# .Net Programming
// A block of user-defined data associated with a file or a
directory
ReparsePoint, Compressed, Offline,
// The file will not be indexed by the operating system's content
indexing service.
NotContentIndexed, Encrypted
}
Program ch05pg06.cs : Program to demonstrate the attribute enumeration in DirectoryInfo class
class ch05pg06
{
static void Main(string[] args)
{
Console.WriteLine("***** Fun with Directory(Info) *****\n");
DirectoryInfo dir = new DirectoryInfo(@"C:\Windows");
// Get all files with a *.bmp extension.
FileInfo[] bitmapFiles = dir.GetFiles("*.bmp");
// How many were found?
Console.WriteLine("Found {0} *.bmp files\n",
bitmapFiles.Length);
// Now print out info for each file.
foreach (FileInfo f in bitmapFiles)
{
Console.WriteLine("***************************\n");
Console.WriteLine("File name: {0} ", f.Name);
Console.WriteLine("File size: {0} ", f.Length);
Console.WriteLine("Creation: {0} ", f.CreationTime);
Console.WriteLine("Attributes: {0} ", f.Attributes);
Console.WriteLine("***************************\n");
}
}
}
Unit 5: Understanding .NET Assemblies & Libraries 10
Prof. Sushant S Sundikar C# .Net Programming
Creating Subdirectories with the DirectoryInfo Type
You can programmatically extend a directory structure using the DirectoryInfo.CreateSubdirectory()
method. This method can create a single subdirectory, as well as multiple nested subdirectories, in a
single function call.
Example Program: ch05pg07.cs :- Program to demonstrate creation of sub directories.
class ch05pg07
{
static void Main(string[] args)
{
Console.WriteLine("***** Fun with Directory(Info) *****\n");
DirectoryInfo dir = new DirectoryInfo(@"C:\MyDirectory");
dir.Create();
// Create \MyFoo off initial directory.
dir.CreateSubdirectory("MyFoo");
// Create \MyBar\MyQaaz off initial directory.
dir.CreateSubdirectory(@"MyBar\MyQaaz");
}
}
Working with the Directory Type
Exposes static methods for creating, moving, and enumerating through directories and
subdirectories. This class cannot be inherited. Use the Directory class for typical operations such as
copying, moving, renaming, creating, and deleting directories. You can also use the Directory class to
get and set DateTime information related to the creation, access, and writing of a directory.
Because all Directory methods are static, it might be more efficient to use a Directory method rather
than a corresponding DirectoryInfo instance method if you want to perform only one action. Most
Directory methods require the path to the directory that you are manipulating.
Program ch05pg08.cs : The following example demonstrates how to move a directory and all its files
to a new directory. The original directory no longer exists after it has been moved.
using System;
using System.IO;
namespace ConsoleApplication
{
Unit 5: Understanding .NET Assemblies & Libraries 11
Prof. Sushant S Sundikar C# .Net Programming
class Program
{
static void Main(string[] args)
{
string sourceDirectory = @"C:\source";
string destinationDirectory = @"C:\destination";
try
{
Directory.Move(sourceDirectory,
destinationDirectory);
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
}
}
}
Working with the FileInfo Class
The FileInfo class allows you to obtain details regarding existing files on your hard drive (time
created, size, file attributes, and so forth) and helps in the creation, copying, moving, and
destruction of files. In addition to the set of functionality inherited by FileSystemInfo are some core
members unique to the FileInfo class, are described in Table 5-3.
Member Meaning in Life
AppendText() Creates a StreamWriter type (described later) that appends text to a file
CopyTo() Copies an existing file to a new file
Create() Creates a new file and returns a FileStream type (described later) to interact
with the newly created file
CreateText() Creates a StreamWriter type that writes a new text file
Delete() Deletes the file to which a FileInfo instance is bound
Directory Gets an instance of the parent directory
DirectoryName Gets the full path to the parent directory
Length Gets the size of the current file or directory
MoveTo() Moves a specified file to a new location, providing the option to specify a new
filename
Name Gets the name of the file
Open() Opens a file with various read/write and sharing privileges
Unit 5: Understanding .NET Assemblies & Libraries 12
Prof. Sushant S Sundikar C# .Net Programming
OpenRead() Creates a read-only FileStream
OpenText() Creates a StreamReader type (described later) that reads from an existing text
file
OpenWrite() Creates a write-only FileStream type
The FileInfo.Create() Method
The first way you can create a file handle is to make use of the FileInfo.Create() method:
public class Program
{
static void Main(string[] args)
{
// Make a new file on the C drive.
FileInfo f = new FileInfo(@"C:\Test.dat");
FileStream fs = f.Create();
// Use the FileStream object Close down file stream.
fs.Close();
}
}
The FileInfo.Open() Method
You can use the FileInfo.Open() method to open existing files as well as create new files with far
more precision than FileInfo.Create(). Once the call to Open() completes, you are returned a
FileStream object.
Example:
static void Main(string[] args)
{
// Make a new file via FileInfo.Open().
FileInfo f2 = new FileInfo(@"C:\Test2.dat");
FileStream fs2 = f2.Open( FileMode.OpenOrCreate,
FileAccess.ReadWrite, FileShare.None);
// Use the FileStream object...
Unit 5: Understanding .NET Assemblies & Libraries 13
Prof. Sushant S Sundikar C# .Net Programming
// Close down file stream.
fs2.Close();
}
TheOpen() method requires three parameters. The first parameter specifies the general flavor of the
I/O request (e.g., make a new file, open an existing file, append to a file, etc.), which is specified
using the FileMode enumeration:
public enum FileMode
{
// Specifies that the operating system should create a new file.
// If the file already exists, a System.IO.IOException is thrown.
CreateNew,
// Specifies that the operating system should create a new file.
// If the file already exists, it will be overwritten.
Create, Open,
// Specifies that the operating system should open a file if it
exists; otherwise, a new file should be created.
OpenOrCreate, Truncate, Append
}
The second parameter, a value from the FileAccess enumeration, is used to determine the
read/write behavior of the underlying stream:
public enum FileAccess
{
Read,
Write,
ReadWrite
}
Finally, you have the third parameter, FileShare, which specifies how the file is to be shared among
other file handlers. Here are the core names:
public enum FileShare
{
None,
Read,
Write,
ReadWrite
}
Unit 5: Understanding .NET Assemblies & Libraries 14
Prof. Sushant S Sundikar C# .Net Programming
Working with the File Type
Provides static methods for the creation, copying, deletion, moving, and opening of files, and aids in
the creation of FileStream objects.
Use the File class for typical operations such as copying, moving, renaming, creating, opening,
deleting, and appending to files. You can also use the File class to get and set file attributes or
DateTime information related to the creation, access, and writing of a file. By default, full read/write
access to new files is granted to all users.
Unlike FileInfo, the File type supports a few unique members
Method Meaning in Life
ReadAllBytes() Opens the specified file, returns the binary data as an array of bytes, and
then closes the file
ReadAllLines() Opens a specified file, returns the character data as an array of strings, and
then closes the file
ReadAllText() Opens a specified file, returns the character data as a System.String, and then
closes the file
WriteAllBytes() Opens the specified file, writes out the byte array, and then closes the file
WriteAllLines() Opens a specified file, writes out an array of strings, and then closes the file
WriteAllText() Opens a specified file, writes the character data, and then closes the file
Example:
class Program
{
static void Main(string[] args)
{
string[] myTasks = {
"Fix bathroom sink",
"Call Dave",
"Call Mom and Dad",
"Play XBox"};
// Write out all data to file on C drive.
File.WriteAllLines(@"C:\tasks.txt", myTasks);
// Read it all back and print out.
foreach (string task in
File.ReadAllLines(@"C:\tasks.txt"))
{
Console.WriteLine("TODO: {0}", task);
}
Unit 5: Understanding .NET Assemblies & Libraries 15
Prof. Sushant S Sundikar C# .Net Programming
}
}
Working with BinaryWriters and BinaryReaders
BinaryReader and BinaryWriter, types allow you to read and write discrete data types to an
underlying stream in a compact binary format. The BinaryWriter class defines a highly overloaded
Write() method to place a data type in the underlying stream. In addition to Write(), BinaryWriter
provides additional members which are displayed in the tables below:
Table 5-5. BinaryWriter Core Members
Member Meaning in Life
BaseStream
BinaryWriter
This read-only property provides access to the underlying stream
used with the object.
Close() This method closes the binary stream.
Flush() This method flushes the binary stream.
Seek() This method sets the position in the current stream.
Write() This method writes a value to the current stream.
Table 5-6. BinaryReader Core Members
Member Meaning in Life
BaseStream
BinaryReader
This read-only property provides access to the underlying stream used
with the object.
Close() This method closes the binary reader.
PeekChar() This method returns the next available character without actually
advancing the position in the stream.
Read() This method reads a given set of bytes or characters and stores them
in the incoming array.
ReadXXXX() The BinaryReader class defines numerous ReadXXXX() methods that
grab the next type from the stream (ReadBoolean(), ReadByte(),
ReadInt32(), and so forth).
Example:
The following example writes a number of data types to a new *.dat file:
static void Main(string[] args)
Unit 5: Understanding .NET Assemblies & Libraries 16
Prof. Sushant S Sundikar C# .Net Programming
{
// Open a binary writer for a file.
FileInfo f = new FileInfo("BinFile.dat");
BinaryWriter bw = new BinaryWriter(f.OpenWrite());
// Print out the type of BaseStream.
// (System.IO.FileStream in this case).
Console.WriteLine("Base stream is: {0}", bw.BaseStream);
// Create some data to save in the file
double aDouble = 1234.67;
int anInt = 34567;
char[] aCharArray = { 'A', 'B', 'C' };
// Write the data
bw.Write(aDouble);
bw.Write(anInt);
bw.Write(aCharArray);
bw.Close();
// Read the data as raw bytes
BinaryReader br = new BinaryReader(f.OpenRead());
int temp = 0;
while (br.PeekChar() != -1)
{
Console.Write("{0,7:x} ", br.ReadByte());
if (++temp == 4)
{
// Write a new line every 4 bytes
Console.WriteLine();
temp = 0;
}
}
Console.WriteLine();
}
Working with StreamWriters and StreamReaders
The StreamWriter and StreamReader classes are useful whenever you need to read or write
characterbased data (e.g., strings). Both of these types work by default with Unicode characters.
StreamReader derives from an abstract type named TextReader, as does the related StringReader
Type. The StreamWriter type (as well as StringWriter, also examined later in this chapter) derives
from an abstract base class named TextWriter. This class defines members that allow derived types
to write textual data to a given character stream. The relationship between each of these new I/O
centric types is shown in figure below:
Unit 5: Understanding .NET Assemblies & Libraries 17
Prof. Sushant S Sundikar C# .Net Programming
Figure: Readers and Writers
To help in your understanding of the core writing capabilities of the StreamWriter and StringWriter
classes, Table below describes the core members of the abstract TextWriter base class.
Member Meaning in Life
Close() Closes the writer and frees any associated resources. In the process, the buffer
is automatically flushed.
Flush() Clears all buffers for the current writer and causes any buffered data to be
written to the underlying device, but does not close the writer.
NewLine Indicates the newline constant for the derived writer class. The default line
terminator is a carriage return followed by a line feed (\r\n).
Write() Writes a line to the text stream without a newline constant.
WriteLine() Writes a line to the text stream with a newline constant.
The derived StreamWriter class provides an appropriate implementation for the Write(), Close(), and
Flush() methods, and it defines the additional AutoFlush property. This property, when set to true,
forces StreamWriter to flush all data every time you perform a write operation. Be aware that you
can gain better performance by setting AutoFlush to false, provided you always call Close() when you
are done writing with a StreamWriter.
Writing to a Text File
static void Main(string[] args)
{
Console.WriteLine("***** Fun with StreamWriter / StreamReader *****\n");
// Get a StreamWriter and write string data.
StreamWriter writer = File.CreateText("reminders.txt");
Unit 5: Understanding .NET Assemblies & Libraries 18
Prof. Sushant S Sundikar C# .Net Programming
writer.WriteLine("Don't forget Mother's Day this year...");
writer.WriteLine("Don't forget Father's Day this year...");
writer.WriteLine("Don't forget these numbers:");
for(int i = 0; i < 10; i++)
writer.Write(i + " ");
// Insert a new line.
writer.Write(writer.NewLine);
// Closing automatically flushes!
writer.Close();
Console.WriteLine("Created file and wrote some thoughts...");
}
Output:
Reading from a Text File
StreamReader type derives from TextReader, which offers the functionality described in Table
below.
Table: TextReader Core Members
Member Meaning in Life
Peek() Returns the next available character without actually changing the position of
the reader. A value of –1 indicates you are at the end of the stream.
Read() Reads data from an input stream.
ReadBlock() Reads a maximum of count characters from the current stream and writes
thedata to a buffer, beginning at index.
ReadLine() Reads a line of characters from the current stream and returns the data as a
string (a null string indicates EOF).
ReadToEnd() Reads all characters from the current position to the end of the stream and
returns them as a single string.
static void Main(string[] args)
{
Console.WriteLine("***** Fun with StreamWriter / StreamReader *****\n");
...
Unit 5: Understanding .NET Assemblies & Libraries 19
Prof. Sushant S Sundikar C# .Net Programming
// Now read data from file.
Console.WriteLine("Here are your thoughts:\n");
StreamReader sr = File.OpenText("reminders.txt");
string input = null;
while ((input = sr.ReadLine()) != null)
{
Console.WriteLine (input);
}
}
Working with StringWriters and StringReaders
Using the StringWriter and StringReader types, you can treat textual information as a stream of in-
memory characters. This can prove helpful when you wish to append character-based information to
an underlying buffer. To illustrate, the following example writes a block of string data to a
StringWriter object rather than a file on the local hard drive:
static void Main(string[] args)
{
Console.WriteLine("***** Fun with StringWriter / StringReader
*****\n");
// Create a StringWriter and emit character data to memory.
StringWriter strWriter = new StringWriter();
strWriter.WriteLine("Don't forget Mother's Day this year...");
strWriter.Close();
// Get a copy of the contents (stored in a string) and pump
// to console.
Console.WriteLine("Contents of StringWriter:\n{0}", strWriter);
}
Because StringWriter and StreamWriter both derive from the same base class (TextWriter), the
writing logic is more or less identical. However, given that nature of StringWriter, be aware that this
class allows you to extract a System.Text.StringBuilder object via the GetStringBuilder() method:
Unit 5: Understanding .NET Assemblies & Libraries 20
Prof. Sushant S Sundikar C# .Net Programming
// Create a StringWriter and emit character data to memory.
StringWriter strWriter = new StringWriter();
// Get the internal StringBuilder.
StringBuilder sb = strWriter.GetStringBuilder();
sb.Insert(0, "Hey!! ");
When you wish to read from a stream of character data, make use of the corresponding
StringReader type, which (as you would expect) functions identically to the related StreamReader
class. In fact, the StringReader class does nothing more than override the inherited members to read
from a block of character data, rather than a file, as shown here:
static void Main(string[] args)
{
Console.WriteLine("***** Fun with StringWriter / StringReader
*****\n");
// Create a StringWriter and emit character data to memory.
StringWriter strWriter = new StringWriter();
...
// Read data from the StringWriter.
StringReader strReader = new StringReader(writer.ToString());
string input = null;
while ((input = strReader.ReadLine()) != null)
{
Console.WriteLine (input);
}
strReader.Close();
}
Programmatically “Watching” Files
Now that you have a better handle on the use of various readers and writers, next you’ll look at the
role of the FileSystemWatcher class. This type can be quite helpful when you wish to
programmatically monitor (or “watch”) files on your system. Specifically, the FileSystemWatcher
type can be instructed to monitor files for any of the actions specified by the NotifyFilters
enumeration.
public enum System.IO.NotifyFilters
{
Attributes, CreationTime,
DirectoryName, FileName,
LastAccess, LastWrite,
Security, Size,
Unit 5: Understanding .NET Assemblies & Libraries 21
Prof. Sushant S Sundikar C# .Net Programming
}
The first step you will need to take to work with the FileSystemWatcher type is to set the Path
property to specify the name (and location) of the directory that contains the files to be monitored,
as well as the Filter property that defines the file extensions of the files to be monitored.
To illustrate the process of watching a file, assume you have created a new directory on your C drive
named MyFolder that contains various *.txt files (named whatever you wish). The following console
application will monitor the *.txt files within the MyFolder and print out messages in the event that
the files are created, deleted, modified, or renamed:
static void Main(string[] args)
{
Console.WriteLine("***** The Amazing File Watcher App *****\n");
// Establish the path to the directory to watch.
FileSystemWatcher watcher = new FileSystemWatcher();
try{
watcher.Path = @"C:\MyFolder";
}
catch(ArgumentException ex) {
Console.WriteLine(ex.Message);
return;
}
// Set up the things to be on the // lookout for.
watcher.NotifyFilter = NotifyFilters.LastAccess
| NotifyFilters.LastWrite
| NotifyFilters.FileName
| NotifyFilters.DirectoryName;
// Only watch text files.
watcher.Filter = "*.txt";
// Add event handlers.
watcher.Changed += new FileSystemEventHandler(OnChanged);
watcher.Created += new FileSystemEventHandler(OnChanged);
watcher.Deleted += new FileSystemEventHandler(OnChanged);
watcher.Renamed += new RenamedEventHandler(OnRenamed);
// Begin watching the directory.
watcher.EnableRaisingEvents = true;
// Wait for the user to quit the program.
Console.WriteLine(@"Press 'q' to quit app.");
while(Console.Read()!='q');
}
The two event handlers simply print out the current file modification:
static void OnChanged(object source, FileSystemEventArgs e)
{
// Specify what is done when a file is changed, created, or deleted.
Console.WriteLine("File: {0} {1}!", e.FullPath, e.ChangeType);
}
Unit 5: Understanding .NET Assemblies & Libraries 22
Prof. Sushant S Sundikar C# .Net Programming
static void OnRenamed(object source, RenamedEventArgs e)
{
// Specify what is done when a file is renamed.
Console.WriteLine("File: {0} renamed to\n{1}", e.OldFullPath,
e.FullPath);
}
To test this program, run the application and open Windows Explorer. Try renaming your files,
creating a *.txt file, deleting a *.txt file, or whatnot. You will see the console application print out
various bits of information regarding the state of the text files within MyFolder( see figure below)