Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Pro CSharp And The .NET 2.0 Platform (2005) [eng]

.pdf
Скачиваний:
111
Добавлен:
16.08.2013
Размер:
10.35 Mб
Скачать

514 C H A P T E R 1 5 U N D E R S TA N D I N G C I L A N D T H E R O L E O F DY N A M I C A S S E M B L I E S

Summary

This chapter provided an overview of the syntax and semantics of CIL. Unlike higher-level managed languages such as C#, CIL does not simply define a set of keywords, but provides directives (used to define the structure of an assembly and its types), attributes (which further qualify a given directive), and opcodes (which are used to implementation type members). You were introduced to the CIL compiler (ilasm.exe) and learned how to alter the contents of a .NET assembly with new CIL code and also the basic process of building a .NET assembly using raw CIL.

The latter half of this chapter introduced you to the System.Reflection.Emit namespace. Using these types, you are able to emit a .NET assembly on the fly to memory. As well, if you so choose, you may persist this in-memory image to a physical file. Recall that many types of System.Reflection.Emit will automatically generate the correct CIL directives and attributes using friendly types such as ConstructorBuilder, TypeBuilder, and so forth. The ILGenerator type can be used to inject the necessary CIL opcodes into a given member. While we do have a number of helper types that attempt to make the process of programming with the CIL opcode set more palatable, you must have an understanding of CIL when programming with dynamic assemblies.

P A R T 4

■ ■ ■

Programming with the

.NET Libraries

C H A P T E R 1 6

■ ■ ■

The System.IO Namespace

When you are creating full-blown desktop applications, the ability to save information between user sessions is imperative. This chapter examines a number of I/O-related topics as seen through the eyes of the .NET Framework. The first order of business is to explore the core types defined in the System.IO namespace and come to understand how to programmatically modify a machine’s directory and file structure. Once you can do so, the next task is to explore various ways to read from and write to character-based, binary-based, string-based, and memory-based data stores.

Exploring the System.IO Namespace

In the framework of .NET, 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, most of which are contained in mscorlib.dll. In addition to the types contained within mscorlib.dll, the System.dll assembly defines additional members of the System.IO namespace (given that all Visual Studio 2005 projects automatically set a reference to both assemblies, you should be ready to go).

Many of the types within the System.IO namespace focus on the programmatic manipulation of physical directories and files. However, additional types provide support to read data from and write data to string buffers as well as raw memory locations. To give you a road map of the functionality in System.IO, Table 16-1 outlines the core (nonabstract) classes.

Table 16-1. 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.

 

Continued

517

518 C H A P T E R 1 6 T H E S YS T E M . I O N A M E S PA C E

Table 16-1. (Continued )

Nonabstract I/O Class Type

Meaning in Life

File

These types are used to manipulate a machine’s set of files. The

FileInfo

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

These types are used to store (and retrieve) textual information

StreamReader

to (or from) a file. These types do not support random file access.

StringWriter

Like the StreamReader/StreamWriter types, these classes also

StringReader

work with textual information. However, the underlying storage

 

is a string buffer rather than a physical file.

 

 

In addition to these creatable class types, System.IO defines a number of enumerations, as well as a set of abstract classes (Stream, TextReader, TextWriter, and so forth), that define a shared polymorphic interface to all descendents. You will read about many of these types in this chapter.

The Directory(Info) and File(Info) Types

System.IO provides four types that allow you to manipulate individual files, as well as interact with a machine’s directory structure. The first two types, Directory and File, expose creation, deletion, copying, and moving operations using various static members. The closely related FileInfo and DirectoryInfo types expose similar functionality as instance-level methods (and therefore must be “new-ed”). In Figure 16-1, notice that the Directory and File types directly extend System.Object, while DirectoryInfo and FileInfo derive from the abstract FileSystemInfo type.

Figure 16-1. The File- and Directory-centric types

C H A P T E R 1 6 T H E S YS T E M . I O N A M E S PA C E

519

Generally speaking, FileInfo and DirectoryInfo are better choices for recursive operations (such as enumerating all subdirectories under a given root), as the Directory and File class members tend to return string values rather than strongly typed objects.

The Abstract FileSystemInfo Base Class

The DirectoryInfo and FileInfo types receive many behaviors from the abstract FileSystemInfo base class. For the most part, the members of the FileSystemInfo class are used to discover general characteristics (such as time of creation, various attributes, and so forth) about a given file or directory. Table 16-2 lists some core properties of interest.

Table 16-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 property

 

gets the name of the directory.

 

 

The FileSystemInfo type also defines the Delete() method. This is implemented by derived types to delete a given file or directory from the hard drive. As well, Refresh() can be called prior to obtaining attribute information to ensure that the statistics regarding the current file (or directory) are not outdated.

Working with the DirectoryInfo Type

The first creatable I/O-centric type you will examine is the DirectoryInfo class. This 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 16-3.

Table 16-3. Key Members of the DirectoryInfo Type

Members

Meaning in Life

Create()

Create a directory (or set of subdirectories), given a path name

CreateSubdirectory()

 

Delete()

Deletes a directory and all its contents

GetDirectories()

Returns an array of strings that represent all subdirectories in the

 

current directory

Continued

520 C H A P T E R 1 6 T H E S YS T E M . I O N A M E S PA C E

Table 16-3. (Continued )

Members

Meaning in Life

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

Once you have created a DirectoryInfo object, you can investigate the underlying directory contents using any of the properties inherited from FileSystemInfo. To illustrate, the following class creates a new DirectoryInfo object mapped to C:\Windows (adjust your path if need be) and displays a number of interesting statistics (see Figure 16-2 for output):

class Program

{

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

}

}

C H A P T E R 1 6 T H E S YS T E M . I O N A M E S PA C E

521

Figure 16-2. Information about your Windows directory

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). While the names of this enum are fairly self-describing, some of the less obvious names are documented here (consult the .NET Framework 2.0 SDK documentation for full details):

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,

//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

}

Enumerating Files with the DirectoryInfo Type

In addition to obtaining basic details of an existing directory, you can extend the current example to use some methods of the DirectoryInfo type. First, let’s leverage the GetFiles() method to obtain information about all *.bmp files located under the C:\Windows directory. This method returns an array of FileInfo types, each of which exposes details of a particular file (full details of the FileInfo type are explored later in this chapter):

522 C H A P T E R 1 6 T H E S YS T E M . I O N A M E S PA C E

class Program

{

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

}

}

}

Once you run the application, you see a listing something like that shown in Figure 16-3. (Your bitmaps may vary!)

Figure 16-3. Bitmap file information

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. To illustrate, here is a block of code that extends the directory structure of C:\Windows with some custom subdirectories:

C H A P T E R 1 6 T H E S YS T E M . I O N A M E S PA C E

523

class Program

{

static void Main(string[] args)

{

Console.WriteLine("***** Fun with Directory(Info) *****\n");

DirectoryInfo dir = new DirectoryInfo(@"C:\Windows");

...

//Create \MyFoo off initial directory. dir.CreateSubdirectory("MyFoo");

//Create \MyBar\MyQaaz off initial directory. dir.CreateSubdirectory(@"MyBar\MyQaaz");

}

}

If you examine your Windows directory using Windows Explorer, you will see that the new subdirectories are present and accounted for (see Figure 16-4).

Figure 16-4. Creating subdirectories

Although you are not required to capture the return value of the CreateSubdirectory() method, be aware that a DirectoryInfo type representing the newly created item is passed back on successful execution:

// CreateSubdirectory() returns a DirectoryInfo item representing the new item.

DirectoryInfo d = dir.CreateSubdirectory("MyFoo"); Console.WriteLine("Created: {0} ", d.FullName);

d = dir. CreateSubdirectory(@"MyBar\MyQaaz"); Console.WriteLine("Created: {0} ", d.FullName);

Working with the Directory Type

Now that you have seen the DirectoryInfo type in action, you can learn about the Directory type. For the most part, the members of Directory mimic the functionality provided by the instance-level members defined by DirectoryInfo. Recall, however, that the members of Directory typically return string types rather than strongly typed FileInfo/DirectoryInfo types.

To illustrate some functionality of the Directory type, the final iteration of this example displays the names of all drives mapped to the current computer (via the Directory.GetLogicalDrives() method) and uses the static Directory.Delete() method to remove the \MyFoo and \MyBar\MyQaaz subdirectories previously created: