One-Dimensional Arrays, the foreach Keyword, Initializing, and the params Keyword
AAHC

Click on A to make all fonts on the page smaller.

Click on A to make all fonts on the page larger.

Click on HC to toggle high contrast mode. When you move your mouse over some bold words in high contrast mode, related words are automatically highlighted. Text is shown in black and white.


In this lesson, we'll learn about arrays (an important programming storage structure), the foreach keyword, and the params keyword. Also, we'll present a final project.

This lesson includes the following sections:

Let's get started!

Array Fundamentals
What Is An Array?

We've used numerous variable types as we've learned the C# language. Also, we've learned the difference between a value data type and a reference data type. Value data type variables store a single value, whereas a reference data type stores a reference to a memory location. The memory location may store a single value, or a data structure that stores multiple values. An array is a reference data type that holds multiple values, or elements, for a specific data type. Even though an array can hold multiple values, each value is referenced through a single variable name.

The following image illustrates the difference between a simple integer variable and an integer array variable. The simple integer variable stores the value of the variable on the stack, whereas the integer array variable stores a reference to a memory location on the stack, which references heap memory, which stores each element of the array.

The illustration shows a way to declare and initialize an array with values. The next section will cover array declaration and initialization in detail.

Array Declaration, Allocation, and Initialization

From the previous illustration, we can see that an array declaration uses the bracket symbols ([]) to indicate we're creating an array. When declaring an array, you have the option to simply declare the array without allocating any array elements. As a reference data type, if no memory is allocated for the array, the value of the array variable is null, indicating that the value stored on the stack for the array does not reference any memory. The code snippet below shows how to declare an integer array that does not reference any heap memory.

Array Declaration
// Integer array declaration with a value of null.               
int[] integerArray;                

How many elements are in the array? As we indicated, no heap memory has been allocated, so there are 0 elements in the array. To allocate heap memory space for the array, we use the new keyword. The following code snippet shows how to declare an integer array, with an initial size of five, which means the integer array has pre-allocated space for five elements.

Array Declaration and Allocation
// Integer array declaration with pre-allocated space for five integer elements.                
int[] integerArray = new int[5];                

When creating memory space for an array, you must specify the size of the array, unless you're also initializing it. We'll learn more about initializing arrays in a moment. The number of elements allocated for an array may be changed using the new keyword, but you must be aware that any data stored in the elements is lost. The code snippet below shows how to change the number of array elements. The default values stored in array elements are whatever is appropriate for the data type of the array. For an integer, the default value is zero, so resizing an array will set all of the element values to zero, regardless of their previous values.

Changing Array Allocation Size
// Integer array declaration with pre-allocated space for five integer elements.                
int[] integerArray = new int[5];                
// Changing the number of array elements to 25.
// Note that any previously stored values in the array elements are erased.
integerArray = new int[25];                

When creating and allocating space for an array, we can also initialize the array elements with values using the curly braces {}:

Initializing An Array
// Integer array declaration with pre-allocated space for five integer elements, with array element initialization.              
int[] integerArray = new int[5]{ 1, 2, 3, 4, 5 };                

When initializing an array, you can omit the array size:

Initializing An Array Without Size
// Integer array declaration with pre-allocated space for five integer elements, with array element initialization, omitting array size.                
int[] integerArray = new int[]{ 1, 2, 3, 4, 5 };                
NoteWhen using an array size during array initialization, the number of array initialization values must match the size of the array. Omitting the array size during initialization is a standard practice, creating an array with a size that matches the number of array initialization values.

Array initialization is so common that the C# language allows you to omit the use of the new keyword:

Initializing An Array Without new Keyword
int[] integerArray = { 1, 2, 3, 4, 5 };                

So, the following array declarations are all equivalent:

Equivalent Array Initialization Statements
int[] integerArray = new int[5]{ 1, 2, 3, 4, 5 };                
int[] integerArray = new int[]{ 1, 2, 3, 4, 5 };                
int[] integerArray = { 1, 2, 3, 4, 5 };                

Although we've listed these as equivalent initialization techniques, we need to point out that these are only equivalent when you first declare an array using the initialization technique without the new keyword. If you already have an array, and you want to initialize the array to a different set of values, you must use the new keyword.

Accessing Array Elements

We've seen how to initialize an array with a group of values. We can also access individual array elements using the square brackets []. Array elements in C# are 0-based, which means that array element numbers start at zero, not one.

Accessing Array Elements
int[] integerArray = { 1, 2, 3, 4, 5 };
integerArray[0] = 10;
integerArray[4] = 50;
// Output the fifth array element value.
System.Console.WriteLine("Fifth array element value is " + integerArray[4]);

The code above changes the value of the first [0] array element from 1 to 10, and the fifth [4] element from 5 to 50. As we mentioned, the 0 element is the first element in an array. If you count the number of array initializer values, the number is five, so the size of the array—the number of array elements—is five. We can access the size of an array using the Length property, if the array has been allocated space. If the array has simply been declared, the C# compiler will display an error. The code snippet below illustrates the use of the Length property of an array.

Array Length Property
// Display the Length, or size, of an array.              
int[] integerArray = { 1, 2, 3, 4, 5 };
System.Console.WriteLine("Array length: " + integerArray.Length);

This code would output the line "Array length: 5."

Coding

Okay, let's see arrays in action!

Select File | New | Project. Change the Name of the project to Arrays and click OK.

Change the entry for Form1.cs to Arrays.cs, and the Form1 Form title bar's Text property to Arrays. Click to save your changes.

Now let's add the controls we'll need.

Add the controls listed below. Change each control Name property to match the Name Property column, and each Text property to match the Text Property column. Arrange the controls similar to the image below.

NoteFor each ComboBox, we've also included the DropDownStyle property that needs to be set to DropDownList. The choices are: Simple, a single, editable entry; DropDown, a multi-selection, editable entry; and DropDownList, a multi-selection, non-editable list.
ObjectName PropertyText PropertyDropDownStyle
LabelweekdayEnglishLabelWeekdays (English): 
ComboBoxweekdaysEnglishComboBox DropDownList
LabelweekdaySpanishLabelWeekdays (Spanish): 
ComboBoxweekdaysSpanishComboBox DropDownList
LabelweekdayFrenchLabelWeekdays (French): 
ComboBoxweekdaysFrenchComboBox DropDownList
LabelweekdayAncientGreekLabelWeekdays (AncientGreek): 
ComboBoxweekdaysAncientGreekComboBox DropDownList
ButtoncloseButtonClose 

Click to save your changes.

Double-click each ComboBox control and the Button control to add the following events to the Arrays form.

ControlEventEvent Name
weekdaysEnglishComboBoxSelectedIndexChangedweekdaysEnglishComboBox_SelectedIndexChanged
weekdaysSpanishComboBoxSelectedIndexChangedweekdaysSpanishComboBox_SelectedIndexChanged
weekdaysFrenchComboBoxSelectedIndexChangedweekdaysFrenchComboBox_SelectedIndexChanged
weekdaysAncientGreekComboBoxSelectedIndexChangedweekdaysAncientGreekComboBox_SelectedIndexChanged
closeButtonClickcloseButton_Click

Next, let's add a couple of methods, and code to the event handlers.

Select the Arrays.cs Form Code Editor, and modify the code as shown below.

NoteOkay, so we thought we'd be funny by adding Ancient Greek. You should type in the sample code, just as we've always recommended, but for the Spanish and Ancient Greek arrays, you can copy the array initialization, unless you enjoy typing letters with diacritic marks and Ancient Greek!
Arrays.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace Arrays
{
    public partial class Arrays : Form
    {
        public Arrays()
        {
            InitializeComponent();
            
            // Set up weekday ComboBox controls.
            populateWeekdayComboBoxes();
        }
        
        private void populateWeekdayComboBoxes()
        {
            // Declare weekday arrays.
            string[] daysOfWeekEnglish = { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" };
            string[] daysOfWeekSpanish = { "Domingo", "Lunes", "Martes", "Miércoles", "Jueves", "Viernes", "Sábado" };
            string[] daysOfWeekFrench = { "Dimanche", "Lundi", "Mardi", "Mercredi", "Jeudi", "Vendredi", "Samedi" };
            string[] daysOfWeekAncientGreek = { "ἡμέρα Ἡλίου (heméra Helíou)", 
                                                "ἡμέρα Σελήνης (heméra Selénes)", 
                                                "ἡμέρα Ἄρεως (heméra Áreos)", 
                                                "ἡμέρα Ἕρμου (heméra Hérmou)", 
                                                "ἡμέρα Διός (heméra Diós)", 
                                                "ἡμέρα Ἀφροδίτης (heméra Aphrodítes)", 
                                                "ἡμέρα Κρόνου (heméra Krónou)" };
            
            // Populate each ComboBox.
            populateWeekdayComboBox(weekdaysEnglishComboBox, daysOfWeekEnglish);
            populateWeekdayComboBox(weekdaysSpanishComboBox, daysOfWeekSpanish);
            populateWeekdayComboBox(weekdaysFrenchComboBox, daysOfWeekFrench);
            populateWeekdayComboBox(weekdaysAncientGreekComboBox, daysOfWeekAncientGreek);
            
            // Set default to the first entry in each ComboBox (0-based index).
            setWeekDayComboBoxes(0);
        }
        
        private void populateWeekdayComboBox(ComboBox comboBox, string[] daysOfWeek)
        {
            // Make sure we have a valid array.
            if (daysOfWeek != null)
            {
                // Clear, then add the days of the week to the ComboBox.
                comboBox.Items.Clear();
                for (int i = 0; i < daysOfWeek.Length; i++)
                {
                    comboBox.Items.Add(daysOfWeek[i].ToString());
                }
            }
        }
        
        private void setWeekDayComboBoxes(int weekDay)
        {
            // Set each ComboBox SelectedIndex.
            weekdaysEnglishComboBox.SelectedIndex = weekDay;
            weekdaysSpanishComboBox.SelectedIndex = weekDay;
            weekdaysFrenchComboBox.SelectedIndex = weekDay;
            weekdaysAncientGreekComboBox.SelectedIndex = weekDay;
        }
        
        private void weekdaysEnglishComboBox_SelectedIndexChanged(object sender, EventArgs e)
        {
            // SelectedIndex changed, so change all of the rest of the ComboBox controls.
            setWeekDayComboBoxes(weekdaysEnglishComboBox.SelectedIndex);
        }
        
        private void weekdaysSpanishComboBox_SelectedIndexChanged(object sender, EventArgs e)
        {
            // SelectedIndex changed, so change all of the rest of the ComboBox controls.
            setWeekDayComboBoxes(weekdaysSpanishComboBox.SelectedIndex);
        }
        
        private void weekdaysFrenchComboBox_SelectedIndexChanged(object sender, EventArgs e)
        {
            // SelectedIndex changed, so change all of the rest of the ComboBox controls.
            setWeekDayComboBoxes(weekdaysFrenchComboBox.SelectedIndex);
        }
        
        private void weekdaysAncientGreekComboBox_SelectedIndexChanged(object sender, EventArgs e)
        {
            // SelectedIndex changed, so change all of the rest of the ComboBox controls.
            setWeekDayComboBoxes(weekdaysAncientGreekComboBox.SelectedIndex);
        }
        
        private void closeButton_Click(object sender, EventArgs e)
        {
            this.Close();
        }
    }
}

Click and to save your changes and run the program. Select a day in an of the Weekdays combo boxes and note how the day changes in the other boxes. Cool! When you finish, click Close. Let's discuss how it works.

OBSERVE: Arrays.cs
.
.
.
namespace Arrays
{
    public partial class Arrays : Form
    {
        public Arrays()
        {
            InitializeComponent();
            
            // Set up weekday ComboBox controls.
            populateWeekdayComboBoxes();
        }
        
        private void populateWeekdayComboBoxes()
        {
            // Declare weekday arrays.
            string[] daysOfWeekEnglish = { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" };
            .
            .
            .
            
            // Populate each ComboBox.
            populateWeekdayComboBox(weekdaysEnglishComboBox, daysOfWeekEnglish);
            populateWeekdayComboBox(weekdaysSpanishComboBox, daysOfWeekSpanish);
            populateWeekdayComboBox(weekdaysFrenchComboBox, daysOfWeekFrench);
            populateWeekdayComboBox(weekdaysAncientGreekComboBox, daysOfWeekAncientGreek);
            
            // Set default to the first entry in each ComboBox (0-based index).
            setWeekDayComboBoxes(0);
        }
        
        private void populateWeekdayComboBox(ComboBox comboBox, string[] daysOfWeek)
        {
            // Make sure we have a valid array.
                if (daysOfWeek != null)
            {
                // Clear, then add the days of the week to the ComboBox.
                comboBox.Items.Clear();
                for (int i = 0; i < daysOfWeek.Length; i++)
                {
                    comboBox.Items.Add(daysOfWeek[i].ToString());
                }
            }
        }
        
        private void setWeekDayComboBoxes(int weekDay)
        {
            // Set each ComboBox SelectedIndex.
            weekdaysEnglishComboBox.SelectedIndex = weekDay;
            weekdaysSpanishComboBox.SelectedIndex = weekDay;
            weekdaysFrenchComboBox.SelectedIndex = weekDay;
            weekdaysAncientGreekComboBox.SelectedIndex = weekDay;
        }
        
        private void weekdaysEnglishComboBox_SelectedIndexChanged(object sender, EventArgs e)
        {
            // SelectedIndex changed, so change all of the rest of the ComboBox controls.
            setWeekDayComboBoxes(weekdaysEnglishComboBox.SelectedIndex);
        }
        
.
.
.

We'll focus on the English version of the arrays, but the discussion applies equally to the other languages.

As you typed in the code, and studied how the code works, you'll notice that we tried to create methods that we could reuse, and that simplified and condensed the techniques we would need to make the program work. As you continue to program, in C# or any language, you will develop the ability to see patterns in your code, and think of programming techniques, such as methods, that will make your code easier to follow. For this sample program, we isolated the types of activities we would need:

  • Create arrays that hold each language days of the week.
  • Populate each ComboBox with the language-appropriate days of the week.
  • Set all ComboBox controls to the same day of the week (initially, and in response to the user changing the day in any of the ComboBox controls).

As a result of identifying these activities, we created three methods to assist in our programming: populateWeekdayComboBoxes, populateWeekdayComboBox, and setWeekDayComboBoxes. Each of these methods corresponds to one of the identified activities. We start the process of creating the arrays by calling populateWeekdayComboBoxes. For our program, we need string arrays, specified by the string[] syntax. We use the shortcut method of omitting the new operator, and simply specify the initialization values, shown in our code {between the braces}.

Within the populateWeekdayComboBoxes method, we call the populateWeekdayComboBox method for each language we want to display, passing the correct ComboBox control (weekdaysEnglishComboBox) and language array (daysOfWeekEnglish). Note that for passing an array, we add the array brackets after the data type (string[]). After each ComboBox control has been populated from the language arrays, we then need to set all of the ComboBox controls to the same item, which we accomplish by calling setWeekDayComboBoxes, passing the number 0, to indicate we want to select the first item in the ComboBox, as ComboBox controls use 0-based indexing, just like arrays.

Within the populateWeekdayComboBox, we need to populate each ComboBox control from the language array. We've created a generic for loop that will loop through each element in the array using the array Length property of the daysOfWeek array parameter, incrementing the i loop counter variable, and then using i to access the correct element of the language array (daysOfWeek[i]). Nnote that we used the < (less-than) boolean comparison operator to tell when to stop iterating through the language array elements, as the array Length property returns seven (7), but 0-based arrays will have elements zero through 6 (0-6). We've also called the ToString() method to illustrate that you need a string data type for a ComboBox, and that you can convert your array data, although since we're already dealing with a string array, this step is unnecessary.

Within the populateWeekdayComboBox, you'll notice that before attempting to iterate through the elements of the language array, we first checked to make sure the language array was not null. Adding this step ensures that our code will not raise an error if someone attempts to call the routine with a language array that has not been allocated.

Finally, note that within each ComboBox event handler, we call the setWeekDayComboBoxes method, passing the index value of the currently selected item in the ComboBox (weekdaysEnglishComboBox.SelectedIndex), thus setting all of the ComboBox controls to the same index value.

So, that's an introduction to arrays! They're very versatile, and very fast. Next, let's move onto some advanced array topics.

Advanced Array Topics
Array Assignments

We've seen how to access individual array elements, but we need to remember that an array is a reference data type, so the rules, or semantics, of a reference data type apply. If you have a reference data type, and you make an assignment to another reference data type, you are not creating a new instance of that data type, but instead are creating a new reference to the first referenced memory. Confused? Let's explain that again, but we'll use arrays to illustrate the point, and a code snippet to help.

In the code snippet below, we declare and initialize an array. Then, we declare a second array, and assign the first array to the second array, in the same line of code. Finally, we declare a third array, and on a separate line of code, assign the first array to the third array.

Array to Array Assignments
// Declare and initialize an array.
int[] myFirstArray = { 1, 2, 3 };
// Declare and assign a second array.
int[] mySecondArray = myFirstArray;
// Declare a third array.
int[] myThirdArray;
myThirdArray = myFirstArray;

All three arrays will be the same. Notice that we omit the square brackets when dealing with array-to-array assignments. Why? We're not dealing with individual array elements, but the entire array. As a reference data type, the array variable is the memory location that stores the actual array elements.

NoteArrays, and other reference data types, share a common thread that we need to remember when it comes to garbage collection. An object is not removed from memory until all of the references to that memory are removed. If you perform reference data type assignments, as we've just performed with our arrays, the reference count must reach zero before the garbage collector will free the memory. When all of our reference variables are in the same local scope, all of the references are cleared when the variables go out of scope. But, if we use class scope for a reference variable, and then assign a local reference variable to a class reference variable, even when the local scope is closed, because we've referenced the memory at the class level, that memory will not be freed until the object itself is deleted, or we clear the reference with our code.
Array Iteration and the foreach Statement

The foreach statement is a useful tool for iterating through the elements of an array (and other collection data types that will be covered later). The foreach statement does more than simply iterate, though—it also enumerates each element as the looping through the elements moves forward, giving you an instance of that specific element. Below is the general syntax of the foreach statement.

The foreach Statement Syntax
foreach (data type identifier in expression)
{
    embedded statement
}     

The data type and identifier serve as the declaration of a read-only iteration variable that represents each element in the expression component. The expression component must be some type of a collection object, and in our case for this lesson, it must be an array. A foreach loops through an array in the index order, starting at zero, and progresses in order through all elements in the array.

Let's see an example. The code below shows where we used a for loop in our Arrays example to iterate through the language array, and the equivalent foreach code.

Replacing for With foreach
// Array iteration using a for loop.
for (int i = 0; i < daysOfWeek.Length; i++)
{
    comboBox.Items.Add(daysOfWeek[i].ToString());
}

// Array iteration using a foreach statement.
foreach (string dayOfWeek in daysOfWeek)
{
    comboBox.Items.Add(dayOfWeek);
}

When iterating through an array or other type of collection object, using a foreach statement results in "cleaner" (easier to read) code, with a benefit that when iterating, the iteration instance variable is read-only.

The params Parameter Modifier

Previously, we've seen the out and ref parameter modifiers, and now we'll complete the list of modifiers with the params modifier. The params modifier indicates that a method parameter may take a variable number of arguments. Remember, an argument is the actual parameters you supply when you call a method, so a variable number of arguments means that you could call a method that uses the params modifier with no arguments, or a hundred arguments, with each argument separated by a comma.

As an alternative, you may also call a method that uses the params modifier passing an array as the single argument, so long as the passed array argument data type matches the declared data type of the method.

The code snippet below uses the params modifier, and illustrates either method of parameter passing using the params modifier.

The params Parameter Modifier
// A method that uses the params modifier.
private void displayNames(params string[] names)
{
    foreach (string name in names)
        System.Console.WriteLine("Name: " + name);
}

// How we would call the above method.
// Passing a comma-delimited list as the parameters.
displayNames("Sam", "Fred", "Bob");

// Creating a compatible string array, and passing array as the parameter.
string[] names = {"Tom", "John", "Kathy"};
displayNames(names);

You may have a method with more than one parameter mixed with a parameter that uses the params modifier, but the parameter that uses the params modifier must be the last parameter, and you may only have one parameter that uses the params modifier. Why? Method arguments are already separated by a comma, so if we had more than one params modifier argument, how would we know where one comma-delimited list ended for one parameter, and the next comma-delimited list began for another parameter? The answer is, we wouldn't be able to tell the difference.

Anonymous Arrays

We've seen anonymous types before, and arrays support the anonymous feature as well. The term anonymous means that the data type is inferred by the compiler from the type of data we're referencing. Below is a code snippet that uses the earlier array examples, and includes the equivalent definition as an anonymous array.

Anonymous Arrays
// Standard array declaration and initialization.
int[] integerArray = new int[]{ 1, 2, 3, 4, 5 };

// Equivalent array declaration and initialization as an anonymous array.
var integerArray = new[]{ 1, 2, 3, 4, 5 };

Anonymous arrays use the var keyword rather than the data type, and require that you use the new keyword.

That concludes this lesson on arrays, so now it's time for a final project to wrap up this course!

Course Project
The Assignment

The final project for this course will be an alarm clock application. Below you'll find all of the information you'll need to create this application, including the required functionality in the Statement of Purpose section. You should be familiar with each of the sections, as they were used in the first course.

The Materials

Statement of Purpose

The application will display a list of user-added daily alarms. Alarms are time-based only. Users will be able to add, edit, and delete alarms. The alarms will be displayed in a list. Alarms may be enabled or disabled, and included in one of a user-selectable category list that includes "Personal" and "Business." Only the hour and minute components of the alarm are set. When an alarm is triggered, the application will display an alarm dialog box that may be dismissed. Alarms are saved to a file when the application closes, and restored from a file when the application starts.

UI Prototype

Class Design Diagrams

NoteThe class diagrams below include the full method signatures. By default, Studio displays only the Name of the item in the class diagram; to expand the member format to include the full signature and return type, right-click the class diagram window and select "Change Members Format."

TOE Chart

TaskObjectEvent
MainForm:
Display/refresh alarmsalarmsListBoxForm Shown
Click (Add, Edit, Delete)
Check/trigger alarmsalarmTimer, timeLabelTick
Alarms ListBox selectioneditButton, deleteButtonselectedIndexChange
Add alarmalarmForm, alarmsListBox, alarmTimerClick
Edit alarmalarmForm, alarmsListBox, alarmTimeralarmsListBox item selected, Click
Delete alarmalarmForm, alarmsListBox, alarmTimeralarmsListBox item selected, Click
Exit applicationexitButtonClick
AlarmForm:
Save alarm (Add, Edit)okButtonClick
Cancel save alarm (Add, Edit)cancelButtonClick

Use Cases - MainForm

  • Start application, restore alarms from file.
  • Add alarm.
  • Edit existing alarm.
  • Delete existing alarm.
  • Exit appliation, save alarms to file.

Use Cases - AlarmForm

  • Add alarm, verify alarm does not already exist before saving.
  • Edit alarm, populate using current alarm data, verify alarm does not already exist before saving.
  • Delete existing alarm, prompt for confirmation before removing alarm.
Coding Requirements

As you create this project, we want to make sure you use specific programming techniques you've learned in this course. The list below includes specific programming requirements in addition to delivering the functionality defined in the Statement of Purpose.

  • You must have an Alarm class, and an Alarms class.
  • You will need to implement most, if not all, of the methods in the class diagram in order for the Alarms application to function.
  • The Alarm class must define a public AlarmCategories string array that contains at least "Personal" and "Business."
  • Use class initializers when creating an instance of the Alarm class.
The Milestones

The project milestones are:

  • Create MainForm Form.
  • Add Alarm class.
  • Add Alarms class.
  • Code MainForm time clock to show the current time using timer Tick event.
  • Code Add alarm functionality.
  • Code alarm trigger in timer Tick event.
  • Code Edit alarm functionality.
  • Code Delete alarm functionality.
  • Code save alarms to file.
  • Code restore alarms from file.
  • Test, test, and test!

As you read through the milestones, take note of any that you might not have thought of. Spend time thinking about why the milestone is in the list. Previously, we mentioned incremental development. These milestones reflect that concept. No matter what project you undertake, breaking the project up into manageable tasks and milestones makes it much easier to complete. It's okay if you miss some of the milestones the first time you make the list (or the second, or third, and so on). As you work through the milestones, you might realize you forgot one. Rather than panic, and feel overwhelmed, simply update your milestones with the new task. Once it's added, you can continue with your work. With practice, you'll get better and better at determining the appropriate milestones and tasks as you continue to develop software.

Testing

You'll note that the last milestone is all about testing (did we emphasize that enough?). You should consider two types of testing: unit testing and integrated testing. Unit testing is the testing you perform as you move through your coding steps. You don't have to perform a test after each milestone, but, as you make significant changes, it might be helpful to test those changes to make sure they work as expected before you move on. In the computer industry, software development is typically split into modules that can be tested as units. New pieces are not considered acceptable until they pass a unit testing process.

Integrated testing is the process of testing a product or piece of product that is composed of much smaller components. Integrated testing detects errors that might not be apparent in unit testing. After you complete all of your milestones, you'll need to make sure you do your final integrated testing.

Hints

Here are some hints that will help you complete this final course project.

Create MainForm GUI Objects First

Your first task should be to set up the GUI for your MainForm. You can refer to the class diagram to determine what components you'll need, but you may prefer to only look at the UI prototype, and try to create your GUI and events without referencing the class diagram. Once you've made your first attempt at designing the MainForm GUI, and adding the necessary events, you can then compare what you created to the class diagram to validate your naming conventions and needed events.

Create Your Alarm and Alarms Classes Initially with Basic Functionality

Once you create your primary GUI, consider creating the Alarm class, and then the Alarms class. From the class diagram, you can see that the Alarms class "has" or consists of Alarm objects in a collection object. The class diagram also shows other methods we added to assist us when using the Alarms class.

Use a Timer Component

In order to trigger an alarm, we need to have a regular timed event occur, which means we need a Timer component. Before tackling adding alarms, add a timer and a timer Tick event that will update the current time clock on the MainForm. You will need to set the Interval property to less than a second to make sure that the seconds update frequently. The Timer component interval is in milliseconds, or thousandths of a second.

Disable and enable Timer Component Appropriately

When using a Timer component, you should be aware of times when you want to disable the timer, such as when the user is adding a new alarm. You can use the Timer Enabled property to stop the timer, and then to restart the timer after adding the new alarm. We included indications of when you should stop and start the timer in the TOE.

Use DialogResult to Process the AlarmForm OK and Cancel Buttons

We've used the Close() method to close a Form, but sometimes you want the Form to remain open until the user has entered the correct information. Rather than use Close(), we can also use the DialogResult Form property. When this property is set in C# code, the Form will close, and the DialogResult value may be checked in the calling code. The code snippet below shows the appropriate DialogResult code to process the Cancel Button. You can use DialogResult.OK for the okButton.

AlarmForm.cs DialogResult for cancelButton
private void cancelButton_Click(object sender, EventArgs e)
{
    this.DialogResult = DialogResult.Cancel;
}

Use Form Accessors to Retrieve Form Values

Once the AlarmForm has closed, you can check if DialogResult.OK was returned, and if so, you should use an AlarmForm accessor to retrieve the values set by the user. You can easily use an Alarm member variable, but you have to remember to set the Alarm properties in AlarmForm before closing the form.

For editing, simply set the Alarm properties before displaying the AlarmForm, and then use the AlarmForm properties to populate the AlarmForm UI elements.

Use DateTime TimeOfDay Property

We are only creating daily alarms, not alarms with specific dates, so make sure you use the DateTime TimeOfDay property to extract only the time component of the DateTime variable for comparisons.

Define a ToString() override method for your Alarm class

Many C# objects will attempt to use the ToString() method of an object for display. If you define a ToString() method for your Alarm class, and then add an Alarm object to your alarmsListBox, you can control what is displayed in the ListBox based on how you define your ToString() method. Below are two snippets of code to explain what we mean.

Alarm.cs ToString() Override Method
public override string ToString()
{
    return this._alarmTime.ToLongTimeString() + " - " + (this._enabled ? "Enabled" : "Disabled");
}

This snippet uses the C# override statement to override the default ToString() method for the Alarm class provided by C#. We haven't discussed the override statement yet, so you can just type in the C# code as described in the code snippet. This code also includes a few very useful programming techniques:

  • Use the DateTime ToLongTimeString() to extract only the Time component of a DateTime object.
  • You can have a return statement with conditional evaluation expressions without having to create method variables.
  • When using the ternary operator with string concatenation, you will need to surround the expression with paranthesis to correctly evaluate the expression.
  • If we want to change what is displayed about an alarm, we only need to change the code in this ToString() method.

So how would we use this ToString() method override? The next snippet shows the steps necessary to take advantage of our overridden method.

MainForm.cs refreshAlarmList Method
private void refreshAlarmList()
{
    // Clear the ListBox.
    alarmsListBox.Items.Clear();
    
    // Add each Alarm to the ListBox, relying on overriden ToString() Alarm method for display.
    for (int i = 0; i < _alarms.Count(); i++)
    {
        alarmsListBox.Items.Add(_alarms.GetAlarm(i));
    }
    
    // Select the first entry in the ListBox, if any alarms remain.
    if (alarmsListBox.Items.Count > 0)
    {
        alarmsListBox.SelectedIndex = 0;
    }
    
    // Enable/disable edit/delete buttons that rely on an item being selecting in ListBox.
    SetButtons(alarmsListBox.Items.Count > 0 && alarmsListBox.SelectedIndex >= 0);
}

Congratulations! You've almost completed the C# Part 2 course! Don't forget to complete the quizzes and submit the final project for the lesson, as you've done for the others.