Reading Files

Read files without causing memory leaks.

Introduction

The best example of an unmanaged resource is a file that resides on a disk. To open it, read it, write to it, or delete it, any programming language must ultimately settle with operating system calls. Because the operating system doesn’t run on top of the CLR, OS calls are considered unmanaged code. Therefore, we must dispose of objects that use such external calls appropriately. Before we start the creation and disposal of file objects, we must first understand how .NET works with files.

The Stream class

A file is nothing but a sequence of bytes. Depending on the file format, this sequence is interpreted differently and the user sees a meaningful output. If we interpret an image as a text, then we get some gibberish output.

In .NET, a sequence of bytes is represented by the Stream class inside the System.IO namespace. It’s an abstract base class for all streams inside .NET. There are two main operations provided by streams:

  • Reading: This represents the transfer of bytes from the external environment (file, network connection, keyboard) into the program. For instance, we can read from the stream and save everything as an array of bytes (byte[]).
  • Writing: This represents the transference of bytes from the application to the external environment (from byte[] to a file, network connection, or an output device).

Depending on the source of a byte sequence, there are various Stream types in .NET. For network access, for example, we have NetworkStream, and for files, we have FileStream. In this lesson, we’re interested in the latter.

Reading with FileStream

The FileStream class provides a stream to interact with a file. It supports read and write operations in both synchronous and asynchronous manners. With FileStream, we can work with both text and binary files.

To create an instance of FileStream, we can use one of the FileStream constructors:

var fileStream = new FileStream(path, mode);

path (string object) is the location of the file we want to open, and mode is an enumeration of type FileMode that can have one of the following values:

  • Append: If the file exists, the text is appended to the end of the file. If the file doesn’t exist, then it’s created. The file is opened only for writing.

  • Create: A new file is created. If a file with the same name already exists, then it’s overwritten.

  • CreateNew: This attempts to create a new file and open it. An exception is thrown if a file with the same name already exists.

  • Open: This opens the file and throws an exception if no such file exists.

  • OpenOrCreate: This opens the file or creates a new one if it doesn’t exist.

  • Truncate: This attempts to open an existing file and overwrite it.

Before using a FileStream object to read a file, let’s first take a look at the files we have at our disposal. For that, we can make use of the Directory static class, which lets us interact with the directory structure:

Get hands-on with 1400+ tech skills courses.