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

Pro Visual C++-CLI And The .NET 2.0 Platform (2006) [eng]

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

278

C H A P T E R 7 C O L L E C T I O N S

Collection<T> and ReadOnlyCollection<T>

Since exposing collections from an object is such a common activity in .NET development, the .NET Framework has provided three base classes to do this: Collection<T>, ReadOnlyCollection<T>, and KeyedCollection<K,V>. There is really nothing new to learn about these collections. All they implement is the minimum functionality required for accessing collections, which we have already gone over in detail.

The Collection<T> class is the base class for simple generic collections and provides implementations for the IList<T>, ICollection<T>, IEnumerable<T>, IList, ICollection, and IEnumerable interfaces.

The ReadOnlyCollection<T> class is the base class for simple generic read-only collections and provides implementations for the IList<T>, ICollection<T>, IEnumerable<T>, IList, ICollection, and

IEnumerable interfaces. Unlike Collection<T> class, there are no properties or methods to update the collection.

The KeyedCollection<K,V> is an abstract class, inherited from Collection<T>, for simple generic key/value collections.

Microsoft suggests that you return one of these three types of collections from your objects if you want to provide a standard, plain-vanilla collection API.

Summary

In this chapter, you took a somewhat detailed look at some of the collections made available by the

.NET Framework class library. You started by looking at the IEnumerable interface, which is common to most collections. Next, you covered all the common collections. You then examined a few of the more specialized collections provided by the .NET Framework class library. You finished by examining the generic type collections, which are new to .NET version 2.0.

In the next chapter, you’re going to look at how the .NET Framework addresses the important areas of file I/O.

C H A P T E R 8

■ ■ ■

Input, Output, and Serialization

Most programs are of little use if there is no way of retrieving input from some source and outputting it to the same or another source. You have several options available for handling input/output (I/O). In this chapter, you will examine file and directory I/O, I/O manipulation, and finally, serialization or the process of storing the state of an object or member to a permanent medium.

There are other I/O mechanisms. For example, this book covers databases, XML, and GUI interfaces in later chapters. Before you get to these more complex I/O systems, you’ll start with simple files. Files are the core of most I/O-related activities in a program.

The first thing you need to look at is the file system. Anybody who plays (oops, I mean works) on a computer sees the file system as an uncomplicated means of placing files wherever he wants them. Usually, the file system is taken for granted. Truthfully, the file system is anything but simple and, without the .NET Framework class library, a developer would see just how complicated it really is.

Once you have the file system under your belt, you will end this chapter with serialization. Serialization is the process of storing a class to the file system for later retrieval. You will see how unbelievably easy this is to do with the .NET Framework class library.

File System Input and Output

When you think of the file system, you need to consider its two parts: files and directories. The .NET Framework class library tries to treat files and directories in a very similar way. But, obviously, there are things that you can do with one that you can’t do with the other. Because of this, the .NET Framework class library has split the functionality of files and directories into two. Well, that is not actually correct, the functionality was split into four: two classes for files and two for directories.

The reason files and directories were split into two classes each is because of the two different ways programmers tend to work with them: either one-time access or over the lifetime of a method, a class, or even an application. One-time access operations on a file or directory really don’t need the overhead of creating an instance of a class to handle the operation. Instead, the use of static methods seems more appropriate. On the other hand, if the file handle or directory handle is going to be around for a while, it makes sense to create a class instance to hold the file handle or directory handle.

The two classes that make up file access are File and FileInfo. The File class contains static methods to access files, whereas you need to create an instance of a FileInfo class to access files. They have much of the same functionality, so selecting one over the other based on functionality does not normally make sense. Instead, you should choose one class over the other based on the number of times the file will be accessed and whether the information being accessed needs to be cached to increase performance. If it will be accessed one time only, then File makes sense. If you need repeated cached access to the file, you should use the FileInfo class.

279

280

C H A P T E R 8 I N P U T , O U T P U T , A N D S E R I A L I Z A T I O N

Managing the File System

As someone who has coded before, you know that you can open, read, and write to files. The .NET Framework class library takes files and the file system in general a step further. It treats files and directories like the objects they are. It provides not only the standard I/O features you have come to expect in a framework, but also ways of dealing with files and directories as a whole. For example, it is possible to copy, move, get information about, and delete complete file and directory objects. With these functions, you now have a way of providing for the maintenance of the file system as a whole and not just the files that make up the system.

FileSystemInfo

You will look at files and directories separately, but you could almost cover them as one, because they have numerous methods and properties in common. In fact, both DirectoryInfo and FileInfo are derived from the same abstract class, FileSystemInfo.

The FileSystemInfo class provides the numerous properties and methods that the DirectoryInfo and FileInfo classes have in common (see Table 8-1).

Table 8-1. Commonly Used FileSystemInfo Class Members

Property/Method

Description

Attributes

Gets or sets attributes associated with the current file system object.

CreationTime

Gets or sets creation date and time of current file system object.

Exists

Determines whether the file system object exists.

Extension

Gets the string extension associated with the current file system object.

FullName

Gets full name of the current file system object. This will include the file or

 

directories path.

LastAccessTime

Gets or sets last access date and time of current file system object.

LastWriteTime

Gets or sets last date and time current file system object was updated.

Name

Gets the name of the file or the last directory of current file system object.

Delete()

Deletes the current file system object.

 

 

As you can see, other than the Delete() method, each of the FileSystemInfo class members in Table 8-1 provides information about the file or directory of the current instance. Some even provide you with update abilities.

Directory and DirectoryInfo

The Directory and DirectoryInfo classes provide you with a means of maintaining the directory structure under which your program has control. If you’ve ever worked directly with the directory structure without the aid of some form of framework, then you’ll quickly come to appreciate the ease with which you can maintain the directory system using the .NET Framework class library. To prove that it’s simple to work with directories in the .NET Framework class library, let’s examine a few of the more common methods and properties.

C H A P T E R 8 I N P U T , O U T P U T , A N D S E R I A L I Z A T I O N

281

Whether you are using the static methods provided by Directory or the properties and member method of DirectoryInfo will determine if you need to call a constructor. Obviously, calling static member methods does not require you to instantiate a class, and thus there is no need for a constructor.

The constructor for the DirectoryInfo class simply takes the full path to the directory you wish to manipulate as a parameter, though the directory doesn’t need to exist if you’re creating it. As you continue, you’ll see that the Directory static member calls have this same full path as the member’s first parameter.

DirectoryInfo ^dir = gcnew DirectoryInfo("C:\\WinNT\\Temp");

To examine the details of a directory using the DirectoryInfo class, you need to implement the inherited properties of the FileSystemInfo class. On the other hand, if you are implementing the Directory class, the static member methods are a bit different.

// DirectoryInfo implementation:

String^

Name

=

dir->FullName;

DateTime

Created

=

dir->CreationTime;

DateTime

Accessed

=

dir->LastAccessTime;

DateTime

Updated

=

dir->LastWriteTime;

FileAttributes

Attributes =

dir->Attributes;

//Directory implementation

//No equivalent for dir->FullName

DateTime Created = Directory::GetCreationTime("C:\\WinNT\\Temp"); DateTime Accessed = Directory::GetLastAccessTime("C:\\WinNT\\Temp"); DateTime Updated = Directory::GetLastWriteTime("C:\\WinNT\\Temp"); // No equivalent for dir->Attributes

Commonly, you are going to want to list all the files and directories that are contained within the current directory. Both Directory and DirectoryInfo provide methods to get all the files and subdirectories separately in two method calls or together in one method call. Notice, though, that the DirectoryInfo implementation returns an Object, whereas the Directory implementation returns complete directory strings.

// DirectoryInfo implementation:

 

array<DirectoryInfo^>^

subDirs

= dir->GetDirectories();

array<FileInfo^>^

files

= dir->GetFiles();

array<FileSystemInfo^>^

dirsFiles = dir->GetFileSystemInfos();

// Directory implementation

 

 

array<String^>^ subDirs

=

Directory::GetDirectories("C:\\WinNT\\Temp");

array<String^>^ files

=

Directory::GetFiles("C:\\WinNT\\Temp");

array<String^>^ dirsFiles =

Directory::GetFileSystemEntries("C:\\WinNT\\Temp");

Three useful methods that Directory has that DirectoryInfo doesn’t are as follows:

String ^currentDirectory = Directory::GetCurrentDirectory(); Directory::SetCurrentDirectory(currentDirectory); array<String^>^ logicalDrives = Directory::GetLogicalDrives();

These methods get and set the current working directory and get all current logical drives on the system.

282

C H A P T E R 8 I N P U T , O U T P U T , A N D S E R I A L I Z A T I O N

A handy auxiliary class that you can use to manipulate the complete directory strings is the Path class. This class contains several static methods to combine, extract, and manipulate path strings. Table 8-2 shows some of the more useful static methods.

Table 8-2. Commonly Used Path Class Members

Method

Description

ChangeExtension()

Changes the extension of the path string.

GetDirectoryName()

Extracts the directory name out of the path string. Notice

 

that for a directory, this method extracts the parent path.

GetExtension()

Gets the extension from the filename contained in the

 

path string.

GetFileName()

Gets the filename or the directory name.

GetFileNameWithoutExtension()

Gets the extension from the filename contained in the

 

path string.

GetFullPath()

Gets the absolute path of the path string.

 

 

To extract the filename out of a complete directory string, you would use the following

GetFileName() method of the Path class:

array<String^>^ files = Directory::GetFileSystemEntries(path); for each (String^ file in files)

{

Console::WriteLine(Path::GetFileName(file));

}

The activities that you will probably do most with directories are checking whether the directory exists, creating a directory, moving or renaming an existing directory, and deleting a directory.

//DirectoryInfo implementation: if (dir->Exists) {}

dir->Create(); // Notice it creates the directory specified by constructor dir->CreateSubdirectory("SubDir");

dir->MoveTo("C:\\WinNT\\TempXXX"); // Move or rename the current directory tree dir->Delete(); // Will fail if directory is not empty

dir->Delete(true); // Deletes the entire directory tree (security permitting)

//Directory implementation

if (Directory::Exists("C:\\WinNT\\Temp")) {} Directory::CreateDirectory("C:\\WinNT\\TempXXX"); Directory::Move("C:\\WinNT\\Temp", "C:\\WinNT\\TempXXX"); Directory::Delete("C:\\WinNT\\TempXXX"); Directory::Delete("C:\\WinNT\\TempXXX", true);

Listing 8-1 shows the DirectoryInfo class in action and demonstrates many of the functionalities described previously.

C H A P T E R 8 I N P U T , O U T P U T , A N D S E R I A L I Z A T I O N

283

Listing 8-1. Working with DirectoryInfo

using namespace System; using namespace System::IO;

using namespace System::Text;

int main(array<System::String ^> ^args)

{

if (args->Length == 0)

{

Console::WriteLine("Usage: DirInfo <Directory>"); return -1;

}

StringBuilder ^tmppath = gcnew StringBuilder();

for each (String^ s in args)

{

tmppath->Append(s); tmppath->Append(" ");

}

String ^path = tmppath->ToString()->Trim();

DirectoryInfo ^dir = gcnew DirectoryInfo(path);

if (!dir->Exists)

{

Console::WriteLine("Directory Not Found"); return -1;

}

Console::WriteLine("Name: {0}", dir->FullName);

Console::WriteLine("Created: {0} {1}", dir->CreationTime.ToShortDateString(), dir->CreationTime.ToLongTimeString());

Console::WriteLine("Accessed: {0} {1}", dir->LastAccessTime.ToShortDateString(), dir->LastAccessTime.ToLongTimeString());

Console::WriteLine("Updated: {0} {1}", dir->LastWriteTime.ToShortDateString(), dir->LastWriteTime.ToLongTimeString());

Console::WriteLine("Attributes: {0}", dir->Attributes);

Console::WriteLine("Sub-Directories:");

284

C H A P T E R 8 I N P U T , O U T P U T , A N D S E R I A L I Z A T I O N

array<DirectoryInfo^>^ subDirs = dir->GetDirectories(); if (subDirs->Length == 0)

Console::WriteLine("\tNone."); else

{

for each (DirectoryInfo^ dinfo in subDirs)

{

Console::WriteLine("\t{0}", dinfo->Name);

}

}

Console::WriteLine("Files:");

array<FileInfo^>^ files = dir->GetFiles(); if (files->Length == 0)

Console::WriteLine("\tNone."); else

{

for each (FileInfo^ finfo in files)

{

Console::WriteLine("\t{0}", finfo->Name);

}

}

return 0;

}

Figure 8-1 shows the results of the DirInfo.exe program.

Figure 8-1. Results of DirInfo.exe

C H A P T E R 8 I N P U T , O U T P U T , A N D S E R I A L I Z A T I O N

285

File and FileInfo

Once you understand how to manage directories, it’s not a big leap to manage files. Most of the properties and methods you use to manage files are identical to those you use to manage directories. The big difference, obviously, is that the class names have changed to File and FileInfo. In addition, there are a few additional file-specific methods added and a couple of directory-specific methods removed. There are also several methods to open up files in different ways. You will learn more about those a little later in the chapter.

Just like directories, having a constructor depends on whether you are using the static methods of File or the instance member methods of FileInfo.

FileInfo ^fileinfo = gcnew FileInfo("C:\\WinNT\\Temp\\file.dat");

Note You could also have coded the previous line as

FileInfo ^fileinfo = gcnew FileInfo("file.dat");

So long as the current directory is C:\WinNT\Temp. You can get and set the current directory with the

Directory class’s GetCurrentDirectory() and SetCurrentDirectory() methods.

Examining the details of a file while implementing the FileInfo class requires the use of the inherited properties of the FileSystemInfo class. You will see very little difference between the file methods and the directory methods. The File class’s static methods are also the same as the directory equivalent, but this time there is a static method to retrieve attributes (see Table 8-3). There is an additional property to get the length of the file out of a FileInfo class but, oddly enough, there is no static method in the File class.

// FileInfo implementation:

 

String^

Name

=

fileinfo->FullName;

DateTime

Created

=

fileinfo->CreationTime;

DateTime

Accessed

=

fileinfo->LastAccessTime;

DateTime

Updated

=

fileinfo->LastWriteTime;

FileAttributes

Attributes =

fileinfo->Attributes;

Int64

Length

=

fileinfo->Length; // Physical, uncompressed, and

 

 

 

// unclustered size

//File implementation

//No equivalent for file->FullName

DateTime

Created

=

File::GetCreationTime("C:\\WinNT\\Temp\\file.dat");

DateTime

Accessed

=

File::GetLastAccessTime("file.dat");

DateTime

Updated

=

File::GetLastWriteTime("file.dat");

FileAttributes

Attributes =

File::GetAttributes("file.dat");

// No equivalent for file->Length;

286 C H A P T E R 8 I N P U T , O U T P U T , A N D S E R I A L I Z A T I O N

Table 8-3. Common File Attributes

Attribute

Description

Archive

This attribute marks a file for archive or backup.

Directory

The file is a directory.

Encrypted

For a file, it means it is encrypted. For a directory, it means that all newly created

 

files in the directory will be encrypted.

Hidden

The file is hidden from normal directory display.

Normal

The file is normal and has no other attributes set. (Note: This attribute is only

 

valid if it is the only attribute set.)

ReadOnly

The file is read-only.

System

The file is part of the operating system.

 

 

Other than open files, which I cover next, the most likely activities you will do with files are check whether a file exists, copy or move an existing file, or simply delete a file. You will find that the methods closely resemble those of the directory.

//FileInfo implementation: if (fileinfo->Exists) {}

fileinfo->CopyTo("C:\\WinNT\\Temp\\file.dat"); fileinfo->CopyTo("file.dat", true); // Overwrite existing fileinfo->MoveTo("C:\\WinNT\\Temp\\file.dat"); // Target file can't exist fileinfo->Delete(); // delete the file

//File implementation:

if (File::Exists("C:\\WinNT\\Temp\\file.dat")) {} File::Copy("C:\\WinNT\\Temp\\file1.dat", "C:\\WinNT\\Temp\\file2.dat"); File::Copy("file1.dat", "file2.dat", true); //overwrite existing File::Move("C:\\WinNT\\Temp\\file1.dat", "file2.dat"); File::Delete("file1.dat");

Caution Even though the documentation sort of suggests otherwise, the destination of the Move() and MoveTo() methods cannot be a directory. The destination must be a nonexistent filename or a complete path including the filename.

Listing 8-2 shows the FileInfo class in action and demonstrates many of the functionalities described previously.

C H A P T E R 8 I N P U T , O U T P U T , A N D S E R I A L I Z A T I O N

287

Listing 8-2. Working with FileInfo

using namespace System; using namespace System::IO;

using namespace System::Text;

int main(array<System::String ^> ^args)

{

if (args->Length == 0)

{

Console::WriteLine("Usage: FileInfo <File>"); return -1;

}

StringBuilder ^tmpfile = gcnew StringBuilder();

for each (String^ s in args)

{

tmpfile->Append(s); tmpfile->Append(" ");

}

String ^strfile = tmpfile->ToString()->Trim();

FileInfo ^fileinfo = gcnew FileInfo(strfile);

if (!fileinfo->Exists)

{

Console::WriteLine("File Not Found"); return -1;

}

Console::WriteLine("Name: {0}", fileinfo->FullName);

Console::WriteLine("Created: {0} {1}", fileinfo->CreationTime.ToShortDateString(), fileinfo->CreationTime.ToLongTimeString());

Console::WriteLine("Accessed: {0} {1}", fileinfo->LastAccessTime.ToShortDateString(), fileinfo->LastAccessTime.ToLongTimeString());

Console::WriteLine("Updated: {0} {1}", fileinfo->LastWriteTime.ToShortDateString(), fileinfo->LastWriteTime.ToLongTimeString());

Console::WriteLine("Length: {0}", fileinfo->Length);

Console::WriteLine("Attributes: {0}", fileinfo->Attributes);

return 0;

}