Learning to Code — Part 10: Structs, Enums, Exceptions & Files
We’re on a roll now, so let’s keep going (he awkwardly said to himself)…
STRUCTS
A struct type is a value type that encapsulates small groups of related variables like the coordinates of a rectangle, or the characteristics of an item in an inventory. A struct is declared like this:
struct Book {
public string title;
public double price;
public string author;
}
Structs are similar to classes, but are much more limited in scope. Another difference is that structs can be instantiated without the new keyword, like this:
struct Book {
public string title;
public double price;
public string author;
}class Program {
static void Main(string[] args) {
Book b;
b.title = “Test”;
b.price = 5.99;
b.author = “David”; Console.WriteLine(b.title);
Console.ReadLine();
}
}
In the code above, within the Main method (of the Program class), a new object of the Book struct is instantiated, and is called b. The Book struct has three, publicly available, variables declared: a string-based one called title, a double-based one called price, and another string-based one called author.
Back within the Main method, the title variable of the object called b is assigned the value Test. The price variable of the object called b is assigned the value 5.99, and the author variable of the object called b is assigned the value David. Finally, the value of b.title, which is Test, is printed to the screen.
The app points out that structs do not support inheritance and cannot contain virtual methods.
Structs can contain methods, properties, indexers, and so on, but cannot contain default constructors (which are constructors without parameters). Conversely, they can contain constructors with parameters. That said, if they do contain a constructor, when you instantiate an object based on one, you do need to use the new keyword, like this:
struct Point {
public int x;
public int y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
}class Program {
static void Main(string[] args) {
Point p = new Point(10, 15);
Console.WriteLine(p.x);
Console.ReadLine();
}
}
In the code above, you’ll notice that since the struct called Point has a constructor called Point, within the Main method, to instantiate an object based on that struct (called p), the new keyword is used. The SoloLearn app wraps up the comparison of structs versus classes in a very understandable way:
In general, classes are used to model more complex behavior, or data, that is intended to be modified after a class object is created. Structs are best suited for small data structures that contain primarily data this is not intended to be modified after the struct is created. Consider defining a struct instead of a class if you are trying to represent a simple set of data.
One last note they make is that all standard C# types (int, double, bool, char, etc.) are actually structs.
ENUMS
The enum keyword is used to declare an enumeration which is a type that consists of a set of named constraints called the enumerator list. It’s kind of like an array in that, the first enumerator has the value 0, and the value of each subsequent enumerator is increased by 1. So in the following…
enum Days {Sun, Mon, Tue, Wed, Thu, Fri, Sat};
…we declare an enumeration called Days, where Sun is 0, Mon is 1, Tue is 2, and so on. You can also assign your own enumerator values as in the following…
enum Days {Sun, Min, Tue=4, Wed, Thus, Fri, Sat};
…we also declare an enumeration called Days and, while Sun is 0 and Mon is 1, we’ve set Tue to be 4 and, as a result, Wed is 5, Thu is 6, and so on. Within your code, you can refer to the value within an enumeration with the dot (.) methodology that we’ve used before:
enum Days { Sun, Mon, Tue, Wed, Thu, Fri, Sat };class Program {
static void Main(string[] args) {
int x = (int)Days.Tue;
Console.WriteLine(x);
}
}
When the code above is executed, it declares an integer-based variable called x, and then, with (int), assigns the values of the Days enumeration to be integers, and then extracts the value that is assigned to Tue, which is 2. It then prints 2 to the screen.
Some other enumeration uses include month names, days of the week, cards in a deck, etc. Enums can also be used with switch statements, as in the following:
enum TrafficLights { Green, Red, Yellow };class Program {
static void Main(string[] args) {
TrafficLights x = TrafficLights.Red; switch (x) {
case TrafficLights.Green:
Console.WriteLine(“Go!”);
break;
case TrafficLights.Red:
Console.WriteLine(“Stop!”);
break;
case TrafficLights.Yellow:
Console.WriteLine(“Caution!”);
break;
}
}
}
In the code above, an enumeration is declared called TrafficLights, and is set with the values Green, Red and Yellow. Within the Main method, a variable of type TrafficLights is declared called x, and is set with the value Red. Since there is no (int), it just takes the actual value. Then as a result of switch (x), it looks through all the cases for an equivalent and, when it finds TrafficLights.Red, proceeds to the next line, printing “Stop!” to the screen. Then, it breaks out of the switch statement, and program ends.
EXCEPTIONS
Moving on, an exception is basically an error or problem that occurs when a program runs and results in the termination of that program. So, exceptions that can occur include:
- A user has entered invalid data (i.e. an integer when the program is looking for a string),
- A file that needs to be opened cannot be found,
- A network connection has been lost in the middle of communications, or
- Insufficient memory or other issues related to physical resources.
The following code produces an exception when it’s executed because a value is requested from an array that doesn’t exist:
int[] arr = new int[] { 4, 5, 7};
Console.Write(arr[8]);
There is no value in index position 8 of the integer-based array called arr. It is possible to write what’s called error handling into your program to deal with most or all possible exceptions.
TRY-CATCH
Within C#, you’re able to use a statement called try-catch to handle exceptions so, when an error occurs, the program will not crash.
try {
int[] arr = new int[] { 4, 5, 8 };
Console.Write(arr[8]);
}catch (Exception e) {
Console.WriteLine(“An error occurred”);
}
In the code above, within the try statement, it basically says try this code and then, in the catch statement, it says if an error occurs, do this thing. So when executed, the above code above will first try:
- To create a new integer-based array called arr and, at the zero index position, assign a value of 4. Then assign a value of 5 at the one index position, and a value of 8 at the two index position.
Then, it tries to print the value at the eight index position of that array called arr.
- Since, in this case, an exception will be encountered, instead of crashing, within the catch statement, it sees that if an Exception of type e (which we’ll get in to in a second) occurs, it prints An error occurred to the screen (which is what it does since there isn’t a value at the eight index position of the array called arr).
As a result of the try-catch statement, the program does not crash. The type of exception is what appears within the parentheses of the catch keyword. In this case we’re using the general Exception type (indicated by the keyword Exception) to handle any kind of exception. The object called e, however, gives us to access some details about the exception. So, within the catch statement, if we switch…
Console.WriteLine(“An error occurred”);
…for…
Console.WriteLine(e.Message);
Index was outside the bounds of the array. will print to the screen. In the code above, we only tested (or tried) for a single type of exception. For a single try block, however, you can have multiple catch blocks, each of which can handle a different type of exception.
int x, y;try {
x = Convert.ToInt32(Console.Read());
y = Convert.ToInt32(Console.Read());
Console.WriteLine(x / y);
}catch (DivideByZeroException e) {
Console.WriteLine(“Cannot divide by 0”);
}catch (Exception e) {
Console.WriteLine(“An error occurred”);
}
When the above code is executed, after declaring two, integer-based variables called x and y, the code within the try block is executed. In this case, there are two catch blocks, which can handle two different types of exceptions. If an exception is generated, and that exception is of the type called DivideByZeroException (which is as a result of a line of code trying to divide a number by zero), the code within the first catch block will be executed, and Cannot divide by 0 will be printed to the screen. If another type of exception is encountered, the general Exception type, called Exception, will be executed, and An error occurred will be printed to the screen.
The SoloLearn app also importantly notes that this general Exception type must always be defined last. Some other exception types include:
- FileNotFoundException
- FormatException
- IndexOutOfRangeException
- InvalidOperationException
- OutOfMemoryException
FINALLY
One last keyword you can use is finally, which is executed whether an exception is encountered or not. So, in the following code…
int result = 0;
int num1 = 8;
int num2 = 4;try {
result = num1 / num2;
}catch (DivideByZeroException e) {
Console.WriteLine(“Error”);
}finally {
Console.WriteLine(result);
}
…when executed, three integer based variables are declared, and values are assigned. Within the try block, the variable called result is set as equal to the value of the variable called num1 (which is 8) being divided by the variable called num2 (which is 4). The outcome is that the variable called result is now equal to 2. Since no exception was encountered, the catch block, which would be executed if a divide by zero exception occurred, is skipped. Whether an exception had occurred, or even if it didn’t (as in the case above), the code within the finally block is executed, which prints the value of result to the screen which, again, is 2.
An example they give of when you could use the finally block is when you program works with files or other resources and, after the execution of the Main functionality of the program, you want to then close or release that resource. The code to do that could be included within that finally block.
One of the things I especially like about the SoloLearn app is that after each individual part of a section, there is a one-question quiz. After this last one, however, it asks what type of class should be used to handle all possible exceptions. The multiple choices are Exception, Main and System. While generally, I knew that the keyword Exception is used to handle all types of exceptions, none of the individual parts of this section ever referred to that keyword as a class. So, just to drive it home…the keyword Exception is a class.
WRITING TO FILES
Finally, and this is probably one of the most exciting topics to me, there is the ability to work with files. As mentioned earlier, programs use the using statement to indicate namespaces, which are like libraries of functionalities.
There’s a namespace called System.IO that has various classes for performing these operations such as creating and deleting files, reading from or writing to files, closing a file, etc. One of those classes is called File:
string str = “Some text”;
File.WriteAllText(“test.txt”, str);
Before explaining what happens when the code above is executed, it’s worth mentioning that when starting a new program in Visual Studio, the following namespaces are given using statements (in other words, loaded) by default:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
Since, as mentioned, the File class is part of the System.IO namespace, I had to add the following to that list:
using System.IO;
Without it, an error would have appeared because C# wouldn’t know how to handle the File keyword. Anyway, as a result of the code, a new, string-based variable is declared, and is assigned the value Some text. Then, the WriteAllText method, which is part of the File keyword, creates a new file called text.txt, and adds the value of the variable called str (which is Some text) to it.
Normally, when you click the Start button within Visual Studio, it does some processing and, as a result, it creates a DLL file (dynamic link library) and an EXE file (executable) with the same name as the program. In this case, I had created a new program called WriteToFiles. So, the executable is called WriteToFiles.exe. When run, a TXT file called test.txt is created within the same location as the EXE file. It’s important to mention that you can indicate a different path if you’d like. Additionally, if the file already exists, it is overwritten.
READING FROM FILES
There is a similar method for reading from files called ReadAllText:
string txt = File.ReadAllText(“test.txt”);
Console.WriteLine(txt);
In this code, a new string-based variable called txt is created and is set equal to the value that comes as a result of executing the ReadAllText method (which is built into the File class as well). It then prints the current value of the txt variable to the screen.
Other methods available within the File class include:
- AppendAllText() — appends text to the end of the file.
- Create() — creates a file in the specified location.
- Delete() — deletes the specified file.
- Exists() — determines whether the specified file exists.
- Copy() — copies the file to a new location.
- Move() — moves a specified file to a new location.
The app also points out that all methods automatically close the file after performing the operation.
So, that completes the second-to-last section of this training and if my train wasn’t arriving at the station, I’d jump right into the last section now. It’s called Generics which, according to TutorialsPoint website, within an article called C# — Generics…
allow you to delay the specification of the data type of programming elements in a class or a method, until it is actually used in the program. In other words, generics allow you to write a class or method that can work with any data type.
OK, sounds interesting. Anyway, let me know if I messed anything up or if there’s anything that needs further explanation. Also, here’s a link to the last article, Learning to Code — Part 9b: Inheritance & Polymorphism.
Web: BIMuzer.com
Twitter: @BIMuzer
LinkedIn: Scott Rosenbloom