Introducing Arrays: Module 04 (Complex Data Structures)
Introducing Arrays: Module 04 (Complex Data Structures)
Introducing Arrays: Module 04 (Complex Data Structures)
Introducing Arrays
An array is a set of objects that are grouped together and managed as a unit. You can think of
an array as a sequence of elements, all of which are the same type. You can build simple arrays
that have one dimension (a list), two dimensions (a table), three dimensions (a cube), and so
on. Arrays in Visual C# have the following features:
Arrays of a particular type can only hold elements of that type. If you need to manipulate a set
of unlike objects or value types, consider using one of the collection types that are defined in
the System.Collections namespace.
When you declare an array, you specify the type of data that it contains and a name for the
array. Declaring an array brings the array into scope, but does not actually allocate any memory
for it. The CLR physically creates the array when you use the new keyword. At this point, you
should specify the size of the array.
To declare a single-dimensional array, you specify the type of elements in the array and use
brackets, [] to indicate that a variable is an array. Later, you specify the size of the array when
you allocate memory for the array by using the new keyword. The size of an array can be any
integer expression. The following code example shows how to create a single-dimensional array
of integers with elements zero through nine.
You can also choose to create an array and initialize it with values at the same time as in the
following example that declares and integer array and assigns values to it. The compiler know
how large to make the array by the number of values in the curly braces:
You can access data in an array in several ways, such as by specifying the index of a specific
element that you require or by iterating through the entire array and returning each element in
sequence.
The following code example uses an index to access the element at index two.
Note: Arrays are zero-indexed, so the first element in any dimension in an array is at index zero.
The last element in a dimension is at index N-1, where N is the size of the dimension. If you
attempt to access an element outside this range, the CLR throws an IndexOutOfRangeException
exception.
You can iterate through an array by using a for loop. You can use the Length property of the
array to determine when to stop the loop.
The following code example shows how to use a for loop to iterate through an array.
Multidimensional arrays
An array can have more than one dimension. The number of dimensions corresponds to the
number of indices that are used to identify an individual element in the array. You can specify
up to 32 dimensions, but you will rarely need more than three. You declare a multidimensional
array variable just as you declare a single-dimensional array, but you separate the dimensions
by using commas. The following code example shows how to create an array of integers with
two dimensions.
// Create an array that is 10 long(rows) by 10 wide(columns)
int[ , ] arrayName = new int[10,10];
In order to access elements in a multidimensional array, you must include all indices as in the
example code here.
Jagged arrays
A jagged array is simply an array of arrays, and the size of each array can vary. Jagged arrays are
useful for modeling sparse data structures where you might not always want to allocate
memory for every item if it is not going to be used. The following code example shows how to
declare and initialize a jagged array. Note that you must specify the size of the first array, but
you must not specify the size of the arrays that are contained within this array. You allocate
memory to each array within a jagged array separately, by using the new keyword.
Introducing enums
An enumeration type, or enum, is a structure that enables you to create a variable with a fixed
set of possible values. The most common example is to use an enum to define the day of the
week. There are only seven possible values for days of the week, and you can be reasonably
certain that these values will never change.
A best practice would be to define your enum directly within a namespace so that all classes in
that namespace will have access to it, if needed. You can also nest your enums within classes or
structs.
By default enum values start at 0 and each successive member is increased by a value of 1.
Creating and Using Enums
To create an enum, you declare it in your code file with the following syntax, which
demonstrates creating an enum called Day, that contains the days of the week:
By default enum values start at 0 and each successive member is increased by a value of 1. As a
result, the previous enum 'Day' would contain the values:
Sunday = 0
Monday = 1
Tuesday = 2
etc.
You can change the default by specifying a starting value for your enum as in the following
example.
In this example, Sunday is given the value 1 instead of the detaul 0. Now Monday is 2, Tuesday
is 3, etc.
The keyword enum is used to specify the "type" that the variable Day will be. In this case, an
enumeration type. Enums support intrinsic data types and can be any one of the following:
- byte
- sbyte
- short
- ushort
- int
- uint
- long
- ulong
In order to change the default data type of your enum, you precede the list with a data type
from the list above, such as:
enum Day : short { Sunday = 1, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday };
The underlying type specifies how much storage will be allocated for each enumerator in the
enum. During compile time, your enum will be converted to numeric literals in your code. If
you are using Visual Studio, the Intellisense feature is fully capable of recognizing your enums
and will display the string values automatically in the IDE as you type the enum name.
It's important to note that you will be required to use an explicit cast if you want to convert
from an enum type to an integral type. Consider this example where the statement assigns the
enumerator Sun to an int type, with a cast, to convert from enum to int.
int x = (int)Day.Sun;
Using an Enum
Using enums has several advantages over using text or numerical types:
Improved manageability. By constraining a variable to a fixed set of valid values, you are less
likely to experience invalid arguments and spelling mistakes.
Improved developer experience. In Visual Studio, the IntelliSense feature will prompt you with
the available values when you use an enum.
Improved code readability. The enum syntax makes your code easier to read and understand.
Each member of an enum has a name and a value. The name is the string you define in the
braces, such as Sunday or Monday. By default, the value is an integer. If you do not specify a
value for each member, the members are assigned incremental values starting with 0. For
example, Day.Sunday is equal to 0 and Day.Monday is equal to 1.
The following example shows how you can use names and values interchangeably:
In Visual C#, a struct is a programming construct that you can use to define custom types.
Structs are essentially lightweight data structures that represent related pieces of information
as a single item. For example:
A struct named Point might consist of fields to represent an x-coordinate and a y-coordinate.
A struct named Circle might consist of fields to represent an x-coordinate, a y-coordinate, and a
radius.
A struct named Color might consist of fields to represent a red component, a green component,
and a blue component.
Most of the built-in types in Visual C#, such as int, bool, and char, are defined by structs. You
can use structs to create your own types that behave like built-in types.
Creating a Struct
You use the struct keyword to declare a struct, as shown by the following example:
//Declaring a Struct
public struct Coffee
{
public int Strength;
public string Bean;
public string CountryOfOrigin;
// Other methods, fields, properties, and events.
}
The struct keyword is preceded by an access modifier— publicin the above example—that
specifies where you can use the type. You can use the following access modifiers in your struct
declarations:
Structs can contain a variety of members including constructors, fields, constants, properties,
indexers, methods, operators, events, and even nested types. Keep in mind that structs are
intended to be lightweight therefore if you find yourself adding multiple methods, constructors,
and events, you should consider using a class instead.
Using a Struct
To create an instance of a struct, you use the new keyword, as shown by the following example:
Instantiating a Struct
Initializing Structs
You might have noticed that the syntax for instantiating a struct, for example, new Coffee(), is
similar to the syntax for calling a method. This is because when you instantiate a struct, you are
actually calling a special type of method called a constructor. A constructor is a method in the
struct that has the same name as the struct.
When you instantiate a struct with no arguments, such as new Coffee(), you are calling the
default constructor which is created by the Visual C# compiler. If you want to be able to specify
default field values when you instantiate a struct, you can add constructors that accept
parameters to your struct.
Adding a Constructor
The following example shows how to use this constructor to instantiate a Coffee item:
Calling a Constructor
// Call the custom constructor by providing arguments for the three required parameters.
Coffee coffee1 = new Coffee(4, "Arabica", "Colombia");
You can add multiple constructors to your struct, with each constructor accepting a different
combination of parameters. However, you cannot add a default constructor to a struct because
it is created by the compiler.
Extending structs
In order to go beyond a simple struct, you can extend it by adding properties and indexers. This
section discusses using properties and indexers in your struct. Again, if you find yourself going
to this extent, evaluate your use of structs against class files.
Creating Properties
In Visual C#, a property is a programming construct that enables client code to get or set the
value of private fields within a struct or a class. To consumers of your struct or class, the
property behaves like a public field. Within your struct or class, the property is implemented by
using accessors, which are a special type of method. A property can include one or both of the
following:
//Implementing a Property
public struct Coffee
{
private int strength;
public int Strength
{
get { return strength; }
set { strength = value; }
}
}
Within the property, the get and set accessors use the following syntax:
The get accessor uses the return keyword to return the value of the private field to the
caller.
The set accessor uses a special local variable named value to set the value of the private
field. The value variable contains the value provided by the client code when it accessed
the property.
//Using a Property
Coffee coffee1 = new Coffee();
// The following code invokes the set accessor. coffee1.Strength = 3;
// The following code invokes the get accessor. int coffeeStrength = coffee1.Strength;
The client code uses the property as if as it was a public field. However, using public properties
to expose private fields offers the following advantages over using public fields directly:
You can use properties to control external access to your fields. A property that includes only a
get accessor is read-only, while a property that includes only a set accessor is write-only.
You can change the implementation of properties without affecting client code. For example,
you can add validation logic, or call a method instead of reading a field value.
public int Strength
{
get { return strength; }
set
{
if(value < 1)
{ strength = 1; }
else if(value > 5)
{ strength = 5; }
else { strength = value; }
}
}
Properties are required for data binding in WPF. For example, you can bind controls to property
values, but you cannot bind controls to field values.
When you want to create a property that simply gets and sets the value of a private field
without performing any additional logic, you can use an abbreviated syntax.
To create a property that reads and writes to a private field, you can use the following syntax:
To create a property that reads from a private field, you can use the following syntax:
To create a property that writes to a private field, you can use the following syntax:
In each case, the compiler will implicitly create a private field and map it to your property.
These are known as auto-implemented properties. You can change the implementation of your
property at any time.
Creating Indexers
In some scenarios, you might want to use a struct or a class as a container for an array of
values. For example, you might create a struct to represent the beverages available at a coffee
shop. The struct might use an array of strings to store the list of beverages.
When you expose the array as a public field, you would use the following syntax to retrieve
beverages from the list:
A more intuitive approach would be if you could access the first item from the menu by using
the syntax myMenu[0]. You can do this by creating an indexer. An indexer is similar to a
property, in that it uses get and set accessors to control access to a field. More importantly, an
indexer enables you to access collection members directly from the name of the containing
struct or class by providing an integer index value. To declare an indexer, you use the this
keyword, which indicates that the property will be accessed by using the name of the struct
instance.
//Creating an Indexer
public struct Menu
{
private string[] beverages;
// This is the indexer.
public string this[int index]
{
get { return this.beverages[index]; }
set { this.beverages[index] = value; }
}
// Enable client code to determine the size of the collection.
public int Length
{
get { return beverages.Length; }
}
}
When you use an indexer to expose the array, you use the following syntax to retrieve the
beverages from the list:
Just like a property, you can customize the get and set accessors in an indexer without affecting
client code. You can create a read-only indexer by including only a get accessor, and you can
create a write-only indexer by including only a set accessor.
Prior to object oriented considerations and class files, programmers created structs in
languages such as C. Some programmers still use structs for storing related information,
although the trend is more towards class files. Because there may still be occasions where a
struct makes sense in your code, you're going to create some structs in this
assignment. Because a struct has a similar layout to a class file, this will provide you with a
layout for the variables in your student, teacher, program, and course aspects.
For this assignment, complete the following tasks. For the structs, just include member
variables and a constructor. Do not create properties at this time. Include all the variables that
you have created up to this point in time.
Assign values to the fields in at least one of the student structs in the array
Using a series of Console.WriteLine() statements, output the values for the student struct that
you assigned in the previous step
For this challenge, expand on the struct array steps to complete the following:
Use an appropriate looping structure to add values to all student structs in the array by
prompting a user of the application to enter values for fields.
For example, if you created fristName, lastName, address, city fields as an example, for each of
the 5 students in the array, you need to prompt the user for each field in the struct, for each
student struct in the array.
Once completed, create another loop to iterate over the array and write the values to the
console window