© The Author(s), under exclusive license to APress Media, LLC, part of Springer Nature 2022
G. ByrneTarget C#https://doi.org/10.1007/978-1-4842-8619-7_16

16. File Handling

Gerard Byrne1  
(1)
Belfast, Ireland
 

File Handling

In the previous chapter, we gained knowledge of string handling and used many of the methods that belong to the String class. We saw some of the new features introduced in C# 10 that related to string handling , and now we will look at another important topic, file handling. File handling is an important skill since there are many uses for it in the commercial environment.

We learned throughout all the previous chapters about the core constructs of a C# program, and we have seen how to write data to an array . We also read that an array is used to temporarily store data in a data structure , but now we will look at how to store the data in the more permanent form of a text file. Once we have seen how to write to a text file, we should easily be capable of writing the data to a database, but for this book we will not get into the setting up of a database.

It is common for developers to interact with files within their applications. If we think about a game application , we will often see that the highest scores are stored, and the store could be a file located on the device where the game is being played. If the top ten scores are written to the file, then we can envisage that the file will have to be amended as new high scores are achieved. With a persistent store, such as a text file, the scores will be read after the device is restarted.

In the same manner when we use a web browser to visit a website, the site might store a cookie on our computer. The cookie could be a session cookie, which is stored temporarily and only exists for the duration of our browser session on this site, or it could be a persistent cookie where a small text file is saved on our computer. Text files are also widely used as log files, and a log file could consist of historical data related to things that occur. An example where we might encounter a log file could be historical data related to when we logged on to our insurance account or a bank account. The insurance company may record data related to the date and time of our log-in, the date and time when we logged out, and what parts of the account we used, for example, the payments window or the statements window. Or it might have been the documents window where we can see a PDF of our policy.

Within .NET we are provided with many “tools” to help us interact with the file system. Files provide a means by which our programs can store and access data. Within .NET the System.IO namespace offers us a collection of classes, methods, enumerations, and types to read and write data streams and files, using either a synchronous or asynchronous approach. In .NET all file handling operations require us to use the System.IO namespace.

An Overview of File Handling

We can think of a file as a series of bytes that exist as a collection of bytes in a named file on a persistent storage. Think of a file in terms of
  • The file path , which contains
    • The drive the file is stored on, which could be a local drive or server

    • The directory the file is located in, within the drive – this may be a nested folder

In C#, when our application code is used to read in from a file or write out to a file, we will use a stream object, which can pass and receive data in the form of bytes. We can think of a stream as a series of bytes, and we will commonly use streams when we are reading or writing a large file where it is more efficient to read the file, chunk by chunk, or where we write to the file, chunk by chunk. Streams offer better performance in our application because in a write operation, the data is written to a stream first and held there until the device being written to is ready to accept the data. Equally, in a read operation, the chunks are sent to a stream as they are read from a file on the device, before they are used by the application requesting the data.

In using streams , it is not always about file streams being used to read from or write to a physical file, for example, a text file (.txt), an image file (.jpg, .jpeg, .bmp, .png), etc. Instead, we could use a different type of stream, and some of the possible stream types are shown in the following:
  • Network A network stream will be used to read from or write to a network socket, where a socket is one of the endpoints in a two-way network communication.

  • Memory – A memory stream will be used to read or write bytes stored in memory.

  • Pipe – A pipe stream will be used to read or write bytes from or to various processes.

  • Buffer – A buffered stream is used to read or write bytes from or to other streams in order to enhance the performance of the operation.

The stream object offers us subclasses that can help when we have to work with any of the streams we have mentioned. Even though there are different classes for dealing with the different types of streams, there is some commonality. The common and regularly used methods will be the following:
  • Read( ) method , which allows us to read data from a stream.

  • Write( ) method , which allows us to write data to a stream.

  • Close( ) method , which frees the file for other processes to use. Not closing the stream means other programs will not have access to the stream and its data.

  • Seek( ) method , which allows us to change position within a stream. We can therefore use this method to read from or write to a position of our choice within the stream.

File Class

Now we will look at the File class , having read a little about streams. The C# File class provides static methods for file operations such as creating a file, copying a file, moving a file, and deleting a file. It also works with the FileStream class to read and write streams. So the File class and the FileStream class are different but work together. Table 16-1 shows some of the methods we might use.
Table 16-1

File class methods

Method

Description

AppendAllLines(filename, lines)

This method will be used to append lines to a file and then close the file. Should the file not exist, then the method will create the file for us, write the required lines to the new file, and then close the newly created file.

AppendAllText(filename, strig)

This method will be used to open a file and append the specified string to the file, closing the file when it has completed the task. Should the file not exist, then the method will create the file for us, write the required string to the new file, and then close the newly created file.

Exists(filename)

This method checks if the specified file exists.

ReadAllBytes(filename)

This method will be used to open a binary file and then read the contents of the file into a byte array before closing the file.

ReadAllLines(filename)

This method will be used to open a file and then read the contents of the file line by line before closing the file.

ReadAllText(filename)

This method will be used to open a file and then read the contents of the file as one block before closing the file.

WriteAllBytes(filename, byte[])

This method will be used to create a new file and then write the contents of the specified byte array to the file before closing the file. Should the file exist, then the method will overwrite it.

WriteAllLines(filename, String[])

This method will be used to create a new file and then write the contents of the specified String array to the file before closing the file. Should the file exist, then the method will overwrite it.

WriteAllText(filename, string)

This method will be used to create a new file and then write the contents of the specified string to the file before closing the file. Should the file exist, then the method will overwrite it.

In Chapter 12 , we read the following point:

In terms of the word static , we will see more about it in Chapter 13 , but for now just accept that static means belonging to this class .

In Chapter 13 , we read the following points:

Using the instance of the class, we have access to the methods and fields of the class that have the public access modifier and are not static.

Adding the full stop after the instance name means those methods and fields that are accessible will be displayed. This is called the dot notation

Well, in .NET the File class contains static methods, and this means they belong to the File class , not any instance of the File class. So, when we wish to use the static methods, we will not need to make an instance of the File class to access and use these methods. Yes, indeed, this is different from what we did in Chapter 13, but it is a perfectly acceptable practice, and there are many classes that follow this paradigm.

In the following exercises, where we will use the File class from the System.IO namespace , we will be using the File class directly to access any methods. The format for calling the methods will be as follows: the word File followed by a period, followed by the method name, for example, File.ReadAllLines( ) , or File. followed by any of the static methods of the File class. A few of the static methods that will appear when the period is added in Visual Studio 2022 are shown in Figure 16-1.

A window displays a dropdown list that contains the file class methods. The file icon is depicted in the lower part.

Figure 16-1

File class methods

Having gained some knowledge about classes and knowing that when we use the File class, we are using the class directly, we will know that we do not need to make an instance of the class. We can say this another way: we do not make an instance of the class.

Let’s code some C# and build our programming muscle.

Add a new project to hold the code for this chapter.
  1. 1.

    Right-click the solution CoreCSharp.

     
  2. 2.

    Choose Add.

     
  3. 3.

    Choose New Project.

     
  4. 4.

    Choose Console App from the listed templates that appear.

     
  5. 5.

    Click the Next button.

     
  6. 6.

    Name the project Chapter16 and leave it in the same location.

     
  7. 7.

    Click the Next button.

     
  8. 8.

    Choose the framework to be used, which in our projects will be .NET 6.0 or higher.

     
  9. 9.

    Click the Create button.

     
Now we should see the Chapter16 project within the solution called CoreCSharp.
  1. 10.

    Right-click the Chapter16 project in the Solution Explorer panel.

     
  2. 11.

    Click the Set as Startup Project option.

     
Notice how the Chapter16 project name has been made to have bold text, indicating that it is the new startup project and that it is the Program.cs file within it that will be executed when we run the debugging.
  1. 12.

    Right-click the Program.cs file in the Solution Explorer window.

     
  2. 13.

    Choose Rename.

     
  3. 14.

    Change the name to FileHandling.cs.

     
  4. 15.

    Press the Enter key.

     
  5. 16.

    Double-click the FileHandling.cs file to open it in the editor window.

     
  6. 17.

    Amend the code as, in Listing 16-1, to have a namespace, a class, and a Main( ) method and import the System and System.IO namespaces.

     
namespace Chapter16
{
  internal class FileHandling
  {
    static void Main(string[] args)
    {
    } // End of Main() method
  } // End of FileHandling class
} // End of Chapter16 namespace
Listing 16-1

Class template with the Main( ) method

We will initially create a string variable to hold the name of the file being used. We will then write the code in a method that checks to see if a file exists, and when we run the application, we will see that a message appears telling us that the file does not exist. After this we will create the file and once again check that the file does exist.
  1. 18.

    Amend the code, as in Listing 16-2, to create string variables at the class level and assign the filenames to them.

     
namespace Chapter16
{
  internal class FileHandling
  {
    /*
    Create a variable to hold the file name. Here the file is
    in the current directory, which in this case means it will
    be in the Chapter 16 folder and then inside the bin folder
    of either the Debug or Release folder depending on how we
    have run the application, Start without debugging or Start
    with debugging which means it goes into the debug folder,
    build application means it will go into the Release folder
    */
    const string policyDetailsFile = "policydetails.txt";
    const string policyDetailsFileNew = "policydetailsnew.txt";
    const string policyDetailsFileCopy = "policydetailscopy.txt";
    static void Main(string[] args)
    {
Listing 16-2

String variable assigned the filename

We will now create a method to check if a file exists using the Exists( ) method, and then we will call the method from the Main( ) method.
  1. 19.

    Amend the code, as in Listing 16-3.

     
    static void Main(string[] args)
    {
      CheckIfTheFileExists();
    } // End of Main() method
    public static void CheckIfTheFileExists()
    {
      // Check if the file exists and display a message
      if (File.Exists(policyDetailsFile))
      {
        Console.WriteLine("Policy details file exists.");
      }
      else
      {
        Console.WriteLine("Policy details file does not exist.");
      }
    }// End of CheckIfTheFileExists() method
  } // End of FileHandling class
} // End of Chapter16 namespace
Listing 16-3

Create a method outside Main( ) and call it from inside Main( )

  1. 20.

    Click the File menu.

     
  2. 21.

    Choose Save All.

     
  3. 22.

    Click the Debug menu.

     
  4. 23.

    Choose Start Without Debugging.

     
Figure 16-2 shows the console window, which displays the message telling us the file does not exist.

A console window displays the message that the file does not exist. It prompts the user to press any key to close the window.

Figure 16-2

File does not exist message

  1. 24.

    Press the Enter key to close the console window.

     
We will now create a method that uses the Create( ) method to create a new file with a specified name and then call the method from the Main( ) method . The Create( ) method will create or overwrite a file with the filename given, and in doing so we are given a FileStream, which gives us read and write access to the file. Once the stream is opened, we need to be sure to close it using the Close( ) method or dispose of the stream in some other way.
  1. 25.

    Amend the code, as in Listing 16-4, to create the method for creating the file.

     
  }// End of CheckIfTheFileExists() method
  public static void CreateTheFile()
  {
    /*
     Create a file called policydetails.txt and close
     the stream that is opened for us
    */
    File.Create(policyDetailsFile).Close();
  }// End of CreateTheFile() method
  } // End of FileHandling class
} // End of Chapter16 namespace
Listing 16-4

Create a new file using the Create( ) method

  1. 26.

    Amend the code, as in Listing 16-5, to call the method from the Main( ) method.

     
    static void Main(string[] args)
    {
      CheckIfTheFileExists();
      CreateTheFile();
    } // End of Main() method
Listing 16-5

Call our new method that creates the file

  1. 27.

    Click the File menu.

     
  2. 28.

    Choose Save All.

     
  3. 29.

    Click the Debug menu.

     
  4. 30.

    Choose Start Without Debugging.

     
The console window will now display the same “file does not exist” message. Just ignore this for now.
  1. 31.

    Press any key to close the console window that appears.

     
  2. 32.

    In the Solution Explorer click the Chapter16 project.

     
  3. 33.

    Click the Show All Files icon, as shown in Figure 16-3.

     

A Solution Explorer Window depicts several icons. The icon for Show All Files is chosen.

Figure 16-3

Show All Files

  1. 34.

    Click the Sync with Active Document button, if the icon is displayed, as shown in Figure 16-4.

     

A Solution Explorer Window depicts several icons. The icon for Sync with Active Document is chosen.

Figure 16-4

Refresh the project files view

  1. 35.

    Expand the bin, Debug, and net6.0 folders.

     
The text file , which we wrote the code to create, should be displayed in the, initially hidden, net6.0 folder, which is inside the Debug folder, which is inside the bin folder, as shown in Figure 16-5.

A folder tree for Chapter 16 exhibits three hidden folders, bin, Debug, and net 6.0. A created file is also noted with the title policy details dot t x t.

Figure 16-5

Text file has been written to its location

The file is not where we might initially have thought it would be. We must not always think about the code we are writing, and its location, but more about the compiled code and where it will be located. This is why our text file is located in the bin file, the binary file folder. Figure 16-6 shows the containing folder where we can see the text file, but also the Chapter16 application file, our program essentially.

A file listing depicts that the policy details text file and the Chapter 16 application file are both located in the bin folder.

Figure 16-6

Location of the compiled code

Writing to a File

WriteAllText( )

We stated earlier that the WriteAllText( ) method is used to create a new file or overwrite the existing file and then write the contents of a string to the file. Now we will code another method to write data to the file we have created and then call the method from the Main( ) method.
  1. 36.

    Amend the code, as in Listing 16-6, to create the new method.

     
    }// End of CreateTheFile() method
    public static void WriteAllTextToTheFile()
    {
      string policyDetailsMessage = "This file will hold details of the customer policies ";
      File.WriteAllText(policyDetailsFile, policyDetailsMessage);
    }// End of WriteAllTextToTheFile() method
  } // End of FileHandling class
} // End of Chapter16 namespace
Listing 16-6

Create the method that will write all text to a file

  1. 37.

    Amend the code, as in Listing 16-7, to call the method from the Main( ) method.

     
    static void Main(string[] args)
    {
      CheckIfTheFileExists();
      CreateTheFile();
      WriteAllTextToTheFile();
    } // End of Main() method
Listing 16-7

Call our new method that writes text to the file

  1. 38.

    Click the File menu.

     
  2. 39.

    Choose Save All.

     
  3. 40.

    Click the Debug menu.

     
  4. 41.

    Choose Start Without Debugging.

     
Figure 16-7 shows the console window, which displays the message telling us the file exists, and now we will be able to check that the data has been written to it.

A console window displays the message that the file exists. It prompts the user to press any key to close the window.

Figure 16-7

File is found and can be written to

  1. 42.

    Press any key to close the console window that appears.

     
  2. 43.

    In the Solution Explorer expand the Chapter16 project.

     
As we have just shown in Figure 16-7, the application code does create the file and we get the message to confirm this. Looking in the file directory structure where our Chapter16 project is located and then looking inside the net6.0 folder, within the Debug folder, within the bin folder, we will see the text file has been created and the text has been written to it.
  1. 44.

    Double-click the policydetails.txt file to open it in the editor window, as shown in Figure 16-8.

     

An editor window displays the data written to the policy details dot t x t file. The string of data reads, This file will hold details of the customer policies.

Figure 16-8

Data written to the text file

WriteAllLines( )

We stated earlier that the WriteAllLines( ) method is used to create a new file or overwrite the existing file with the contents of a string array. Now we will code another method to write all the data from a string array to the file we have created and then call the method from the Main( ) method.
  1. 45.

    Amend the code, as in Listing 16-8, to create a method.

     
    }// End of WriteAllTextToTheFile() method
    public static void WriteAllLinesToTheFile()
    {
      string[] policyDetailsArray = {"Home insurance", "ID123456", "199.99"};
      File.WriteAllLines(policyDetailsFile, policyDetailsArray);
    }// End of WriteAllLinesToTheFile() method
  } // End of FileHandling class
} // End of Chapter16 namespace
Listing 16-8

Create the method that will write the array contents to the text file

  1. 46.

    Amend the code, as in Listing 16-9, to call the method from the Main( ) method.

     
    static void Main(string[] args)
    {
      CheckIfTheFileExists();
      CreateTheFile();
      WriteAllTextToTheFile();
      WriteAllLinesToTheFile();
    } // End of Main() method
Listing 16-9

Call our new method that writes all lines to the file

  1. 47.

    Click the File menu.

     
  2. 48.

    Choose Save All.

     
  3. 49.

    Click the Debug menu.

     
  4. 50.

    Choose Start Without Debugging.

     
  5. 51.

    Press any key to close the console window that appears.

     
  6. 52.

    In the Solution Explorer click the Chapter16 project.

     
  7. 53.

    Double-click the policydetails.txt file to open it in the editor window, as shown in Figure 16-9.

     

An editor window displays the array data written to the policy details dot t x t file. The array data consist of three lines.

Figure 16-9

Data from array written to the text file, overriding existing text

WriteAllBytes( )

We stated earlier that the WriteAllBytes method is used to create a new file or overwrite the existing file with the contents of a byte array . Now we will code another method where we will

  • Create a string variable and assign it a string of text.

  • Convert the string to bytes using the GetBytes( ) method from System.Text.Encoding.ASCII and place the bytes in a byte array.

  • Use the WriteAllBytes( ) method to write the bytes to a new text file.

  • Display the bytes by iterating the byte array and displaying each element, which will be a byte value from 0 to 255.

  1. 54.

    Amend the code, as in Listing 16-10, to create the method to perform the steps listed previously.

     
    }// End of WriteAllLinesToTheFile() method
    public static void WriteAllBytesToTheFile()
    {
      string policyMessage = "All policies";
      byte[] policyMessageAsData =
      System.Text.Encoding.ASCII.GetBytes(policyMessage);
      File.WriteAllBytes(policyDetailsFile, policyMessageAsData);
      Console.WriteLine("The bytes written to the file are");
      foreach (byte letterCode in policyMessageAsData)
      {
        Console.WriteLine("The byte written is – " + letterCode);
      }
    }// End of WriteAllBytesToTheFile() method
  } // End of FileHandling class
Listing 16-10

Create the method to write a byte array’s contents to the text file

  1. 55.

    Amend the code, as in Listing 16-11, to call the WriteAllBytesToTheFile( ) method from the Main( ) method.

     
    static void Main(string[] args)
    {
      CheckIfTheFileExists();
      CreateTheFile();
      WriteAllTextToTheFile();
      WriteAllLinesToTheFile();
      WriteAllBytesToTheFile();
    } // End of Main() method
Listing 16-11

Call our new method that writes a byte array’s contents to the text file

  1. 56.

    Click the File menu.

     
  2. 57.

    Choose Save All.

     
  3. 58.

    Click the Debug menu.

     
  4. 59.

    Choose Start Without Debugging.

     
The console window will appear, as shown in Figure 16-10, and display the ASCII byte values that are stored in the byte array.

A console window displays 12 lines of Ascii byte codes.

Figure 16-10

The bytes from the byte array are displayed

  1. 60.

    Press any key to close the console window that appears.

     
  2. 61.

    In the Solution Explorer click the Chapter16 project.

     
  3. 62.

    Double-click the policydetails.txt file to open it in the editor window, as shown in Figure 16-11, and see that the data is written to the file.

     

An editor window displays the data from the array written to the policy details dot t x t file. The data reads, All policies.

Figure 16-11

Data from array written to the text file

We have completed code demonstrating the following methods from the File class:
  • WriteAllText( )

  • WriteAllLines( )

  • WriteAllBytes( )

These File class methods will cause any existing text to be overwritten, but if we wanted to keep the existing text, then we could use the Append versions of these methods:
  • AppendAllText( ) or AppendAllTextAsync( )

  • AppendAllLines( ) or AppendAllLinesAsync( )

  • AppendAllBytes( )

We will now write code demonstrating some methods from the File class that perform the “opposite” operations from the write methods we have used. These will be read methods and we will be looking to use
  • ReadAllText( )

  • ReadAllLines( )

  • ReadAllBytes( )

There are also async versions of these three methods: ReadAllTextAsync( ), ReadAllLinesAsync( ) and ReadAllBytesAsync( ).

Reading from a File

ReadAllText( )

We stated earlier that the ReadAllText( ) method is used to open a file and then read the contents of the file as one block, before closing the file. Now we will code another method to read all the text from a new file, which we will create, and then call the method from the Main( ) method.

Create the new text file.
  1. 1.

    Right-click the policydetails.txt file in the net6.0 folder, which is inside the Debug folder, inside the bin folder, in the Solution Explorer window.

     
  2. 2.

    Choose Copy.

     
  3. 3.

    Right-click the net6.0 folder.

     
  4. 4.

    Choose Paste.

     
  5. 5.

    Right-click the newly pasted policydetails – Copy.txt file.

     
  6. 6.

    Choose Rename.

     
  7. 7.

    Rename the file policydetailsnew.txt.

     
  8. 8.

    Double-click the policydetailsnew.txt file to open it in the editor window.

     
  9. 9.

    Amend the text file by adding some additional lines of text. Listing 16-12 shows some additional lines added to the one line that previously existed.

     
All policies
Policy AU123456
Policy HO987654
Policy BO234580
Policy LI675423
Listing 16-12

Additional lines added to the policydetailsnew.txt file

We will now add the new method to the FileHandling.cs file and then call it from the Main() method .
  1. 10.

    Amend the FileHandling.cs code, as in Listing 16-13, to create the method.

     
    }// End of WriteAllBytesToTheFile() method
    public static void ReadAllTextFromTheFile()
    {
     // Open the file to be read from
     string textReadFromFile =
     File.ReadAllText(policyDetailsFileNew);
     Console.WriteLine("The data read is: " + textReadFromFile);
    }// End of ReadAllTextFromTheFile() method
  } // End of FileHandling class
} // End of Chapter16 namespace
Listing 16-13

Create the method to read all contents of the text file

  1. 11.

    Amend the code, as in Listing 16-14, to call the method from the Main( ) method.

     
    static void Main(string[] args)
    {
      CheckIfTheFileExists();
      CreateTheFile();
      WriteAllTextToTheFile();
      WriteAllLinesToTheFile();
      WriteAllBytesToTheFile();
      ReadAllTextFromTheFile();
    } // End of Main() method
Listing 16-14

Call our new method that reads all contents of the text file

  1. 12.

    Click the File menu.

     
  2. 13.

    Choose Save All.

     
  3. 14.

    Click the Debug menu.

     
  4. 15.

    Choose Start Without Debugging.

     
The console window will appear, as shown in Figure 16-12, and show the contents of the file that has been read.

A console window displays the data read from the new file. It starts with a text that reads, The data read is, and then proceeds to list 4 policies.

Figure 16-12

Data read from the file is displayed

A console window displays the data read from the lines of the file. Instead of listing the policy number only, the text follows the format, The policy number is, then mentions the policy number.

Figure 16-13

Data read from the lines of the file is displayed

  1. 16.

    Press any key to close the console window that appears.

     

ReadAllLines( )

We stated earlier that the ReadAllLines( ) method is used to open a file and then read the contents of the file line by line before closing the file. Now we will code another method to
  • Read all the lines from the file we have created

  • If it is the first line, display the text as it is.

  • If it is not the first line, use the Substring( ) method to read the policy number part of the line, which starts at character 7.

  • Display a message showing the policy number.

We will then call the method from the Main( ) method.
  1. 17.

    Amend the code, as in Listing 16-15, to create the method.

     

A console window displays some of the data read from the file to a byte array. The byte codes are given equivalent Ascii characters which become the basis for the account number.

Figure 16-14

Some of the data read from the file to a byte array

}// End of ReadAllTextFromTheFile() method
public static void ReadAllTextLinesFromTheFile()
{
  // Open the file to be read from
  string[] textLinesReadFromFile = File.ReadAllLines(policyDetailsFileNew);
  int lineCounter = 0;
  foreach (string lineReadFromFile in textLinesReadFromFile)
  {
    if(lineCounter > 0)
    {
      string policyNumber = lineReadFromFile.Substring(7);
      Console.WriteLine("The policy number is " + policyNumber);
    }
    else
    {
      Console.WriteLine(lineReadFromFile);
    }
    lineCounter++;
  }// End of foreach
}// End of ReadAllTextLinesFromTheFile() method
  } // End of FileHandling class
} // End of Chapter16 namespace
Listing 16-15

Create the method to read all lines of the text file

  1. 18.

    Amend the code, as in Listing 16-16, to call the method from the Main( ) method.

     
    static void Main(string[] args)
    {
      CheckIfTheFileExists();
      CreateTheFile();
      WriteAllTextToTheFile();
      WriteAllLinesToTheFile();
      WriteAllBytesToTheFile();
      ReadAllTextFromTheFile();
      ReadAllTextLinesFromTheFile();
    } // End of Main() method
Listing 16-16

Call our new method that reads all lines of the text file

  1. 19.

    Click the File menu.

     
  2. 20.

    Choose Save All.

     
  3. 21.

    Click the Debug menu.

     
  4. 22.

    Choose Start Without Debugging.

     
  1. 23.

    Press any key to close the console window, as shown in Figure 16-13.

     

ReadAllBytes( )

We stated earlier that the ReadAllBytes( ) method is used to open a binary file and then read the contents of the file into a byte array before closing the file. Now we will code another method to read all the bytes from the file we created and add them to a byte array. We will then call the method from the Main( ) method.
  1. 24.

    Amend the code, as in Listing 16-17, to create the method.

     
    }// End of ReadAllTextLinesFromTheFile() method
    public static void ReadAllBytesFromTheFile()
    {
      /*
      Open the file to read from and read all the bytes placing
      them in a byte string
      */
      byte[] bytesReadFromTheFile = File.ReadAllBytes(policyDetailsFileNew);
      foreach (byte byteReadFromFile in bytesReadFromTheFile)
      {
        Console.WriteLine("The byte is " + byteReadFromFile);
      }
    }// End of ReadAllBytesFromTheFile() method
  } // End of FileHandling class
} // End of Chapter16 namespace
Listing 16-17

Create the method to read all bytes of the text file

  1. 25.

    Amend the code, as in Listing 16-18, to call the method from the Main( ) method.

     
    static void Main(string[] args)
    {
      CheckIfTheFileExists();
      CreateTheFile();
      WriteAllTextToTheFile();
      WriteAllLinesToTheFile();
      WriteAllBytesToTheFile();
      ReadAllTextFromTheFile();
      ReadAllTextLinesFromTheFile();
      ReadAllBytesFromTheFile();
    } // End of Main() method
Listing 16-18

Call our new method that reads all bytes of the text file

  1. 26.

    Click the File menu.

     
  2. 27.

    Choose Save All.

     
  3. 28.

    Click the Debug menu.

     
  4. 29.

    Choose Start Without Debugging.

     
  1. 30.

    Press any key to close the console window, as shown in Figure 16-14.

     

Copy a File

Up to now we have been reading from and writing to files without any errors, exceptions, occurring. However, when we attempt to read and write files , things can go wrong, for example:
  • The file might not exist.

  • The file might be readonly when we attempt to write to it.

  • The file may be damaged.

  • We may have the filename spelled incorrectly.

C# gives us a try catch block that can help us avoid getting an exception error when we perform file handling processes . The C# try catch block uses the try and catch keywords, and the try catch block is placed around the code that we think could throw an exception. When an exception is thrown, the try catch block will handle the exception and therefore ensure that our application does not cause an unhandled exception. Since we are dealing with files and we are using the System.IO namespace , we can use the catch section to catch any IO exceptions.

Copy( )

The Copy( ) method is used to copy an existing file to a new file, but it cannot overwrite a file with the same name, unless specifically told to. The Copy( ) method has two forms:
  • Copy(String, String)

This form copies an existing file to a new file, but overwriting a file with the same name is not permitted.
  • Copy(String, String, Boolean)

This form copies an existing file to a new file, and overwriting a file with the same name is permitted.
  1. 31.

    Amend the code, as in Listing 16-19, to create a method that uses the Copy( ) method from the File class to copy the specified source file to the specified destination file.

     
    }// End of ReadAllBytesFromTheFile() method
    public static void CopyAFile()
    {
      // Use a try catch construct to catch any exceptions
      try
      {
        /*
        Copy the contents of the source file policydetailsnew.txt
        to the destination file policydetailscopy.txt
        */
      File.Copy(policyDetailsFileNew,policyDetailsFileCopy,true);
       Console.WriteLine("The copying process was successful.");
      } // End of try block
      catch (IOException exceptionFound)
      {
        Console.WriteLine("Copying failed with exception:");
        Console.WriteLine(exceptionFound.Message);
      } // End of catch block
    }// End of CopyAFile() method
  } // End of FileHandling class
} // End of Chapter16 namespace
Listing 16-19

Create the method to copy the file

  1. 32.

    Amend the code, as in Listing 16-20, to call the method from the Main( ) method.

     
    static void Main(string[] args)
    {
      CheckIfTheFileExists();
      CreateTheFile();
      WriteAllTextToAFile();
      WriteAllLinesToAFile();
      WriteAllBytesToAFile();
      ReadAllTextFromAFile();
      ReadAllTextLinesFromAFile();
      ReadAllBytesFromAFile();
      CopyAFile();
    } // End of Main() method
Listing 16-20

Call our new method that copies the file

  1. 33.

    Click the File menu.

     
  2. 34.

    Choose Save All.

     
  3. 35.

    Click the Debug menu.

     
  4. 36.

    Choose Start Without Debugging.

     
Figure 16-15 shows the output that indicates a successful copying process.

A console window displays the output and a message that the copying process was successful.

Figure 16-15

File success message

  1. 37.

    Press any key to close the console window that appears.

     
Looking in the net6.0 folder, inside the Debug folder, inside the bin folder, we should see the file policydetailscopy as shown in Figure 16-16.

A folder tree of Chapter 16 displays the copied file on the lower part of the list. The copied file is highlighted and is named, policy details copy dot t x t.

Figure 16-16

File copied

Delete a File

In a similar manner to reading a file or writing a file where we could get an exception, when we wish to delete a file, we could get an exception, so we will use the try catch block around the process used to delete a file.

Delete( )

The Delete( ) method is used to delete an existing file.
  1. 38.

    Amend the code, as in Listing 16-21, to create a method that uses the Delete( ) method from the File class to delete the specified source file.

     
    }// End of CopyAFile() method
    public static void DeleteAFile()
    {
      // Use a try catch construct to catch any exceptions
      try
      {
        // Delete the file policydetailscopy.txt
        File.Delete("policydetailscopy.txt");
        Console.WriteLine("The file deletion was successful.");
      } // End of try block
      catch (IOException exceptionFound)
      {
       Console.WriteLine("File deletion failed with exception:");
       Console.WriteLine(exceptionFound.Message);
      } // End of catch block
    }// End of DeleteAFile() method
  } // End of FileHandling class
Listing 16-21

Create the method to delete the file

  1. 39.

    Amend the code, as in Listing 16-22, to call the method from the Main( ) method.

     
    static void Main(string[] args)
    {
      CheckIfTheFileExists();
      CreateTheFile();
      WriteAllTextToAFile();
      WriteAllLinesToAFile();
      WriteAllBytesToAFile();
      ReadAllTextFromAFile();
      ReadAllTextLinesFromAFile();
      ReadAllBytesFromAFile();
      CopyAFile();
      DeleteAFile();
    } // End of Main() method
Listing 16-22

Call our new method that deletes a file

  1. 40.

    Click the File menu.

     
  2. 41.

    Choose Save All.

     
  3. 42.

    Click the Debug menu.

     
  4. 43.

    Choose Start Without Debugging.

     
The output, as shown in Figure 16-17, shows the deletion was successful.

A window displays two lines of text. The second line is framed and it reads, The file deletion was successful.

Figure 16-17

Deletion successful

  1. 44.

    Press any key to close the console window that appears.

     
Looking in the net6.0 folder, inside the Debug folder, inside the bin folder, we should see the copied file has been deleted as shown in Figure 16-18.

Two folder trees of Chapter 16 are placed side by side. The one on the left contains the copied file, and the one on the right does not have it.

Figure 16-18

File deleted

StreamReader Class

The C# StreamReader class provides methods that allow us to read the characters of a file into a stream. There is also a StreamWriter class that allows us to write to a stream, and we will discuss this in the next section. StreamReader and StreamReader hide us from some of the complexities of streams, and we will look at this in the “FileStream” section.

Stream

First, we might want to think about what a stream is and why we would use one. Well, let's think about downloading a film from our favorite online store. Do we expect the whole video to be downloaded before we watch it? Well, the answer is probably not, and this is where the idea of a stream comes in. The stream will act as an intermediary, between the sender and the receiver, where the sender starts delivering the movie, chunk by chunk, and the stream accepts the chunk and starts to pass the data to the receiver. The speed at which the sender can send the movie data and the speed at which the receiving device can accept the data could well be different, so the stream holds the data and “feeds” the receiver at a speed that suits it. In the meantime, as this streaming process is taking place, we are able to start watching the movie as the first chunks have arrived, and the other chunks will be available when we need them. We should therefore think about the stream as having the objective of making for a smooth process when performing a read and write operation. Also, we should remember that it’s not just movies that the principle of streaming applies to.

Synchronous and Asynchronous

Second, we might want to think about what synchronous and asynchronous mean when we apply the terms to file handling operations. Synchronous is also known as sync, and asynchronous is also known as async, and there will be different methods used depending on whether we use sync or async. As an initial thought, we might wish to think of the concepts as follows:
  • With synchronous processes, the first process or task needs to complete before the next process starts. We therefore have the first process “blocking” other processes while it completes its task fully.

  • With asynchronous processes, the first process or task does not need to complete before the next process starts. The processes can run in parallel. One process does not block the other processes, and we therefore will hear the term “non-blocking” in the context of asynchronous processing.

Now, if we think about synchronous and asynchronous approaches within our C# code, we might see that the synchronous approach could cause delays in the execution of a process or program. Many programmers will therefore use an asynchronous approach to overcome the delays. In programming we will hear mention of threading, the use of multiple threads where each thread will return after making a request, so that the program can get on with performing the other processes it has to perform.

Looking at another programming language called JavaScript, we will see that it is by default synchronous and single-threaded, meaning our JavaScript code could not create new threads and run in parallel. On the other hand, when Node.js was created, it introduced a non-blocking input and output (I/O) environment, and there is the concept of a callback , which is used to inform one process when its work has been completed. Think of it like the “chef” who takes the order for eggs and toast from child 1. They process the order by making the eggs and toast, and then they call the first child when their order is complete, a callback. We have probably experienced a callback or asynchronous process in our everyday life.

StreamReader Class Methods

StreamReader uses the TextReader class as its base class and has methods and one property that can help us perform operations on our stream of data. Table 16-2 shows some of the StreamReader methods we might use, but we should be aware that there are many overloads of these methods that can be used.
Table 16-2

StreamReader class methods and property

Method

Description

Read( )

This method will be used to read the next set of characters from an input stream and then move to the next.

ReadAsync( )

This method will be used to asynchronously read a sequence of bytes from a stream and then move to the next position in the stream to read the next sequence of bytes.

ReadLine( )

This method will be used to read a line of characters from a stream returning the data read as a string.

ReadLineAsync( )

This method will be used to read a line of characters from a stream in an asynchronous manner returning the data read as a string.

ReadToEnd( )

Reads all characters from the current position to the end of the stream.

Property

EndOfStream

This property of the class returns a value that indicates if we are at the end of the stream. It is a Boolean value, so it is either true or false.

StreamWriter Class

The C# StreamWriter class provides methods that allow us to write characters to a stream with specific encoding and by default uses UTF-8 encoding. UTF-8 is used as an encoding mechanism for Unicode, and Unicode is a standard that assigns a unique number to every character. UTF is an acronym for Unicode Transformation Format.

The StreamWriter class inherits from the TextWriter class so it makes use of the methods in this TextWriter class. Table 16-3 shows some of the StreamWriter methods we might use, but we should be aware that there are many overloads of these methods that can be used.
Table 16-3

StreamWriter class methods and property

Method

Description

Write( )

This method will be used to write a sequence of bytes to a stream and then advance to the position after this sequence of bytes.

WriteAsync( )

This method will be used to asynchronously write a sequence of bytes to a stream and then advance to the position in the stream after this sequence of bytes.

WriteLine( )

This method will be used to write a formatted string and a new line to a stream.

WriteLineAsync( )

This method will be used to asynchronously write a sequence of bytes to a stream followed by a line terminator.

Property

 

EndOfStream

Gets a value that indicates if the current stream position is at the end of the stream. The return value is true if the current stream position is at the end of the stream; otherwise, it will be false.

Reading from a Stream

We will now use the StreamReader Read( ) method and the EndOfStream property in some code. Initially, we will create a method that uses the Read( ) method to read the specified text and then display the byte data. When we open a stream, we should always close it, so we will use the Close( ) method to do this.
  1. 1.

    Amend the code, as in Listing 16-23.

     
    }// End of DeleteAFile() method
    public static void UseStreamReaderRead()
    {
      try
      {
        if (File.Exists(policyDetailsFile))
        {
          // Create an instance of the StreamReader class
          StreamReader myStreamReader = new StreamReader(policyDetailsFile);
          /*
          Iterate the instance of the StreamReader while there
          is data, which means the Peek() method returns an
          integer greater than 0
          */
          while (myStreamReader.Peek() > 0)
          {
            Console.Write(myStreamReader.Read() + " ");
          }
          myStreamReader.Close();
        }
        else
        {
        }
      }
      catch (Exception exceptionFound)
      {
        Console.WriteLine("Process failed with the exception:");
        Console.WriteLine(exceptionFound.Message);
      }
      Console.WriteLine();
    }// End of UseStreamReaderRead() method
  } // End of FileHandling class
} // End of Chapter16 namespace
Listing 16-23

Read( ) and Peek( ) to read file characters, close StreamReader

  1. 2.

    Call the method from the Main( ) method, as in Listing 16-24.

     
      CopyAFile();
      DeleteAFile();
      UseStreamReaderRead();
    } // End of Main() method
Listing 16-24

Call our new method that reads the specified text

  1. 3.

    Click the File menu.

     
  2. 4.

    Choose Save All.

     
  3. 5.

    Click the Debug menu.

     
  4. 6.

    Choose Start Without Debugging.

     
The output, as shown in Figure 16-19, shows the bytes of the file that has been read. The file contains the words All policies, which is 12 characters, so we see 12 byte codes. Each of the bytes represents an ASCII character, for example, 65 is A and 32 is a space.

A console window displays 12 byte codes corresponding to Ascii characters. The byte codes return the phrase, All policies.

Figure 16-19

Bytes read from file – these bytes represent the string All policies

  1. 7.

    Press any key to close the console window that appears.

     

Writing to a Stream

Rather than using the try catch block, we will use a different approach to illustrate that a using block can be used if we wish to have garbage collection (GC) handled for us. Using is very helpful and can be used with the StreamReader and StreamWriter classes. With the using block, we wrap the StreamWriter process inside it. Using handles disposal of any objects that are not required; it is not a try catch being used to catch exceptions, but rather a way to ensure disposal of objects we are not using.

We will discuss the using statement at the end of this chapter, but for now we will use the pre–C# 8 version of the using statement, and later we will see how there is an alternative way to use the using without the curly braces.

We will now use the StreamWriter WriteLine( ) method in some code along with the using block:

  • We will create the using block.

  • Using will be given an instance of the StreamWriter class.

  • In creating the instance of the StreamWriter class, we will use the constructor, which will accept a true attribute, and this indicates that the content should be appended to the file, rather than overwriting the contents.

  • We will use the WriteLine( ) method to write some lines to the file.

  • We will also add an extra WriteLine( ) so our new data starts on a new line.

  1. 8.

    Amend the code, as in Listing 16-25.

     
 }// End of UseStreamReaderRead() method
 public static void UseStreamWriterWriteInUsingBlock()
 {
   using (StreamWriter myStreamWriter =
                new StreamWriter(policyDetailsFile, true))
   {
     try
     {
     if (File.Exists(policyDetailsFile))
     {
       myStreamWriter.WriteLine("");
       myStreamWriter.WriteLine("Auto insurance");
       myStreamWriter.WriteLine("ID987654");
       myStreamWriter.WriteLine("299.99");
       Console.WriteLine("The data has been written to file");
        }
        else
        {
        }
      }
      catch (Exception exceptionFound)
      {
       Console.WriteLine("Process failed with the exception:");
          Console.WriteLine(exceptionFound.Message);
        }
      } // End of using block
    }// End of UseStreamWriterWriteInUsingBlock() method
  } // End of FileHandling class
} // End of Chapter16 namespace
Listing 16-25

Making use of the using block

  1. 9.

    Call the method from the Main( ) method, as in Listing 16-26.

     
      DeleteAFile();
      UseStreamReaderRead();
      UseStreamWriterWriteInUsingBlock();
    } // End of Main() method
Listing 16-26

Call our new method

  1. 10.

    Click the File menu.

     
  2. 11.

    Choose Save All.

     
  3. 12.

    Click the Debug menu.

     
  4. 13.

    Choose Start Without Debugging.

     
  5. 14.

    Press any key to close the console window that appears.

     
  6. 15.

    Double-click the policydetails.txt file to open it in the editor window.

     
Figure 16-20 shows the lines of text have been added, but because of the other code we have, the policydetails.txt starts blank and our new method adds its data. We therefore do not see the effect of the true, which allows appending data to the file. If we wanted to see this append effect, we would need to comment out the other method calls in the Main( ) method. But for now, we will accept that appending has occurred.

An editor window displays the data that has been appended.

Figure 16-20

WriteLine() method inside a using block

Great, the StreamWriter class has been used with its WriteLine( ) method to write data to the file.

Async Methods and Asynchronous Programming

When we use input and output operations such reading from and writing to a database or text file, we should consider using asynchronous programming. To assist in using asynchronous programming, the C# language offers us Task-based Asynchronous Pattern (TAP). This effectively means we do not have to worry about the callbacks we discussed earlier, as they will be handled for us. When we use this asynchronous pattern, two keywords are important, async and await. The important concepts to understand are as follows:
  • We must use the async keyword to convert our method into an async method, which then allows us to use the await keyword within the body of the method. We should not use async on void methods, and we should use Task as the return type for non-void methods.

  • The await keyword acts to put the caller on hold while the async method is performing what it needs to perform, but in the meantime our program can do other things until we get control back.

Listing 16-27 shows an example of using Task and await, while Listing 16-28 shows how we call the async method that returns a Task.

So how can using asynchronous programming help us when reading and writing files? Well, writing to a file in an asynchronous manner allows our program to continue doing other things while the file is being written to. If we are writing small files, the asynchronous approach will hardly be noticeable, but when the files become much larger, which will be the case in many business applications, it will make a big difference. The same principle applies when reading a large file in an asynchronous manner. Our program can get on with doing other things while it awaits the end of the reading process.

WriteLineAsync

We will now use the StreamWriter WriteLineAsync( ) method to write data. This is almost the same code as in Listing 16-25, but we are using a WriteLineAsync() method instead of the ordinary WriteLine( ) method, and we will still have our code in a using block. We read earlier that the WriteLineAsync( ) method is used to asynchronously write a sequence of bytes to a stream followed by a line terminator.

  1. 16.

    Amend the code as in Listing 16-27.

     
    }// End of UseStreamWriterWriteInUsingBlock() method
    public static async Task<int> WriteCharactersAsynchronously()
    {
      int count = 0;
      using (StreamWriter myStreamWriter = File.CreateText("asynctextfile.txt"))
      {
        await myStreamWriter.WriteLineAsync("Life insurance");
        await myStreamWriter.WriteLineAsync("LF123456");
        await myStreamWriter.WriteLineAsync("99.99");
        count += 3;
      }
      return count;
    }// End of WriteCharactersAsynchronously() method
  } // End of FileHandling class
} // End of Chapter16 namespace
Listing 16-27

Writing data asynchronously with WriteLineAsync( )

  1. 17.

    Call the method from the Main( ) method, as in Listing 16-28.

     
      UseStreamWriterRead();
      UseStreamWriterWriteInUsingBlock();
      Task<int> task = WriteCharactersAsynchronously();
    } // End of Main() method
Listing 16-28

Call our new method that writes data asynchronously

  1. 18.

    Click the File menu.

     
  2. 19.

    Choose Save All.

     
  3. 20.

    Click the Debug menu.

     
  4. 21.

    Choose Start Without Debugging.

     
  5. 22.

    Press any key to close the console window that appears.

     
  6. 23.

    Double-click the asynctextfile.txt file, in the net6.0 folder, to open it in the editor window.

     
Figure 16-21 shows the lines of text have been added, just like in the last example, but this time using an asynchronous method. Remember that we will not see any real difference when the code runs because we will be writing small amounts of data.

An editor window displays the lines of text added using the asynchronous method.

Figure 16-21

Data written to new file in an asynchronous manner

FileStream

When looking at this section, we need to be aware that we have used the StreamReader and StreamWriter classes and they have shielded us from certain “complexities.” Now we are going to see how we can do things that the StreamReader and StreamWriter shield us from.

FileModes

A file stream needs to understand the file mode to be used, and the FileMode enumeration holds the modes. For now, we will not concern ourselves with what an enumeration is, as we will have a full chapter on enumerations later in the book. The FileMode fields assist us in manipulating files. Table 16-4 shows some of the constants included in the FileMode enum, along with a short description, while Table 16-5 shows some of the stream methods we might see,
Table 16-4

The FileMode fields, which dictate the file mode

Field

Description

Open

This will open the file.

OpenOrCreate

This will open the file if it exists and creates a new file if it does not exist.

Append

This will open the file and seeks the end of the file. If the file does not exist, it will be created.

Create

This will create a new file, and if the file already exists, it will be overwritten.

CreateNew

This will create a new file, and if the file already exists, an exception is thrown – more about this shortly.

Truncate

This will open a file and truncate it so the size is zero bytes.

Table 16-5

Stream class methods

Method

Description

Seek( )

This method will be used to set the position within a stream.

Read( )

This method will be used to read a sequence of bytes from the stream and then move by the number of bytes read to the new position within the stream.

ReadAsync( )

This method will be used to asynchronously read a sequence of bytes from the stream and then move by the number of bytes read to the new position within the stream.

Write( )

This method will be used to write a sequence of bytes to the stream and then move by the number of bytes written to the new position within the stream.

WriteAsync( )

This method will be used to asynchronously write a sequence of bytes to the stream and then move by the number of bytes written to the new position within the stream.

We will now create a method that will
  • Create a byte array from a string of characters.

  • Display the elements of the byte array.

  • Create a file to hold the byte data and use an appropriate method to add the bytes to it.

  • Use the Seek( ) method to move to a position in the FileStream, the stream.

  • Read each byte from this new position to the end of the stream.

  • Display the data that has been read.

Having displayed the byte elements, we will be able to confirm that the data is displayed from the new position found using the Seek( ) method.
  1. 24.

    Amend the code, as in Listing 16-29, to create the method.

     
    } // End of WriteCharactersAsynchronously() method
    public static void FileStreamSeekReadAndWrite()
    {
      const string claimsFileName = "claims.dat";
      int startPosition = 6;
      // Create a string and then convert it to a byte array
      string claimantsName = "Gerry Byrne";
      byte[] claimantsByteArray =
       System.Text.Encoding.ASCII.GetBytes(claimantsName);
      Console.WriteLine("The bytes written to the file are");
      // Iterate the byte array and display each byte
      foreach (byte byteInTheArray in claimantsByteArray)
      {
        Console.WriteLine(byteInTheArray);
      } // End of foreach block
      Console.WriteLine("The bytes read from the file are");
      using (FileStream fileStream = new FileStream(claimsFileName, FileMode.Create))
      {
        for (int counter = 0; counter< claimantsByteArray.Length; counter++)
        {
          fileStream.WriteByte(claimantsByteArray[counter]);
        } // End of for block
        // Move to new position in the file stream
        fileStream.Seek(startPosition, SeekOrigin.Begin);
        for (int counter = 0; counter< fileStream.Length - startPosition; counter++)
        {
          Console.WriteLine(fileStream.ReadByte());
        } // End of for block
      } // End of using block
    } // End of FileStreamSeekReadAndWrite() method
  } // End of FileHandling class
} // End of Chapter16 namespace
Listing 16-29

Using the Seek() method

  1. 25.

    Call the method from the Main( ) method, as in Listing 16-30.

     
      UseStreamWriterWriteInUsingBlock();
      Task<int> task = WriteCharactersAsynchronously();
      FileStreamSeekReadAndWrite();
    } // End of Main() method
Listing 16-30

Call our new FileStreamSeekReadAndWrite( ) method

  1. 26.

    Click the File menu.

     
  2. 27.

    Choose Save All.

     
  3. 28.

    Click the Debug menu.

     
  4. 29.

    Choose Start Without Debugging.

     
Figure 16-22 shows the console output.

A console window displays five bytes written vertically and framed with a note, start at position. There is another set of five bytes written vertically, framed, and with a note, characters at position 6, 7, 8, 9, and 10.

Figure 16-22

Data read from file starting at index 6

  1. 30.

    Press any key to close the console window that appears.

     

Chapter Summary

So, finishing this chapter on file handling, we should be aware that C# file handling is taken care of by the namespace System.IO. We have used some of the methods of the File class including Exists( ), Create( ), ReadAllText( ), WriteAllText( ), Copy( ), and Delete( ). We also looked at the StreamReader class with methods such as Read( ), ReadAsync( ), and the synchronous and asynchronous concepts in programming. Likewise, we looked at the StreamWriter class and the methods WriteLine( ) and WriteLineAsync( ). The FileStream class was then introduced when we looked at the Seek( ), ReadByte( ), and WriteByte( ) methods, but we should be aware that all stream types work in roughly the same way. We also looked at exception handling with the try catch code block and the using statement.

It is great to be able to read from and write to a text file, and we could in the future apply a similar concept to reading from and writing to a database.

We are making fantastic progress in programming our C# applications. We should be proud of our achievements. In finishing this very important chapter, we have increased our knowledge further and we are advancing to our target.

An illustration of concentric circles with a text below that reads, our target is getting closer. The circles at the center are shaded in green.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.117.74.44