Models, Classes, and Objects
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 more about using C# as an object-oriented programming (OOP) language to model the real world, to represent that model using classes, to create instances of those classes by creating objects, and to control the interaction with our object model via a specific programming interface. We will create a virtual aquarium program as we work through these concepts. Let's get started!

Object-Oriented Programming Constructs

Just as with other OOP languages, C# includes programming constructs that aid in creating software that represents objects in the real world. Let's discuss these topics on our way to creating our virtual aquarium!

Models: A World View

The word model has many definitions related to OOP. Initially, we may consider the typical definition of model as a representation of something in the real world. Just as an architect might construct a small scale model to represent a final, real-world construction, computer programmers also use the idea of a model to represent what is to be coded. The closer the programming model resembles the real world, the easier it is to understand the implementation.

To help us understand the concept, let's consider an aquarium. An aquarium may consist of a tank, the water inside the tank, and fish. Other components might be rocks, pebbles, and other decoration items, as well as an air pump to oxygenate the water. Because we intend to use this model to create a virtual aquarium, all we need are the tank and the fish to populate it.

When developing software, we take the idea of a model even further. Software architects use a variety of representational models to assist in the development process. For example, the objects that are involved are typically modeled using Unified Modeling Language (UML) to construct a UML Model Diagram, also known as an object model, or class diagram. We'll be using UML later. We'll introduce other models as the lessons progress; you should note that the word "model" does not have to be part of the description, but that something is being represented by the "model."

Classes: A Blueprint and More

Along with a model, an architect composes a set of blueprints to further represent and define what is to be constructed. Software development also includes blueprints, but we call them classes. A C# class includes properties related to the object represented, as well as methods to set and retrieve the values of these properties, and other interactions related to the usage of the class as a virtual object.

Now, let's explore these concepts by creating our virtual aquarium!

Example: The Alphabet Aquarium

Create a new Visual Studio Windows Forms Application named AlphabetAquarium.

Change the name Form1.cs to AlphabetAquarium.cs. Change the Form1 Form title bar's Text property to Alphabet Aquarium. Click to save your changes.

We need to add controls to our Form; for this lesson we'll introduce two new controls: SplitContainer and Panel. We'll use the SplitContainer control to split the Form into two horizontal columns. The left column will be our aquarium, and the right column will be for our other controls. We'll use the Panel control as our actual aquarium. Using the Panel control will make it easier for us to set the color (to represent the water), and to add the fish.

Make the Form about twice as wide by clicking and dragging the right edge to the right. (Or click on the Form, and in the Properties Window change the Width property to 600. If you have your Property Window sorted by Category, you will find the Width property under the Size category.)

Drag a SplitContainer control onto the Form. You do not need to rename the SplitContainer control. Resize the panels within the SplitContainer by clicking and dragging the center line between Panel1 and Panel2, making Panel2 about 1/3 the size of Panel1.Drag a Panel control from the Toolbox onto Panel1. Click the small black right arrow at the top right of the Panel to bring up the Panel Tasks popup menu, and select Dock in Parent Container to have the Panel fill the entire Panel1 column of the SplitContainer control:

 

Change the Panel control's Name property to fishTankPanel, and the BackColor property to Cyan (under the Web tab of the Color Dialog box that appears).

Modify Panel2 by adding the controls listed in the first table below. Change each control's Name property to match the Name Property column, and each Text property to match the Text Property column. Arrange the controls as shown.

ObjectName PropertyText Property
ComboBoxcolorsComboBox
PictureBoxcolorPictureBox
ComboBoxfishComboBox
ButtonaddFishButtonAdd Fish
ButtonclearFishButtonClear Fish
LabelfishLabelFish Count:
LabelfishCountLabel0
ButtonexitButtonExit

Further modify the ComboBox controls as shown:

ObjectPropertyValue
colorsComboBoxDropDownStyleDropDownList
fishComboBoxDropDownStyleDropDownList

When you finish, click to save your changes.

Now that we have the interface created, we need to add the appropriate C# code. Earlier, we introduced the model components for this project: a fish tank, and the fish. So, let's create these two classes.

Right-click on the AlphabetAquarium project item in the Solution Explorer, select Add, and then select Class.... In the Add New Item dialog box, change the Name from Class1.cs to Fish.cs, then click Add.


 

In the Code Editor for Fish.cs, modify the code as shown.

Note Note that we have not made the Fish class public as we have in past lessons. By default, when you create a new class, Studio omits the public access modifier. When a class is declared without an access modifier, the access level is internal. Internal access level indicates that the class can only be accessed from within the same assembly, but not from other assemblies. Public access would allow access to the class from other assemblies. Unless you are creating code that may be called from other assemblies, omitting the class access modifier is acceptable. If you decide later that you need to access the class from another assembly, you can always add back the public access modifier. You might also note that many developers and development groups prefer that you always indicate the access modifier to prevent confusion. For additional information, refer to the following web pages: Access Modifiers and Assemblies and the Global Assembly Cache.
Fish.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;

namespace AlphabetAquarium
{
    class Fish
    {
        private Color _fishColor;
        public Color FishColor
        {
            get { return _fishColor; }
            set { _fishColor = value; }
        }
        
        private int _xPosition;
        public int XPosition
        {
            get { return _xPosition; }
            set { _xPosition = value; }
        }
        
        private int _yPosition;
        public int YPosition
        {
            get { return _yPosition; }
            set { _yPosition = value; }
        }
        
        private string _fishLetter;
        public string FishLetter
        {
            get { return _fishLetter; }
            set { _fishLetter = value; }
        }
        
        public Fish(string fishLetter, int xPosition, int yPosition, Color fishColor)
        {
            // If no letter specified, use "X."
            if (fishLetter.Length == 0)
                fishLetter = "X";
            _fishLetter = fishLetter;
            
            // Ensure the position is >= 0.
            if (xPosition < 0)
                xPosition = 0;
            _xPosition = xPosition;
            
            if (yPosition < 0)
                yPosition = 0;
            _yPosition = yPosition;
            
            // Set the fish color.
            _fishColor = fishColor;
        }
    }
}

Click to save your changes. Let's discuss this code.

OBSERVE: Fish.cs
.
.
.
using System.Drawing;

namespace AlphabetAquarium
{
    class Fish
    {
        private Color _fishColor;
        public Color FishColor
        {
            get { return _fishColor; }
            set { _fishColor = value; }
        }
        
        private int _xPosition;
        public int XPosition
        {
            get { return _xPosition; }
            set { _xPosition = value; }
        }
        
        private int _yPosition;
        public int YPosition
        {
            get { return _yPosition; }
            set { _yPosition = value; }
        }
        
        private string _fishLetter;
        public string FishLetter
        {
            get { return _fishLetter; }
            set { _fishLetter = value; }
        }
        
        public Fish(string fishLetter, int xPosition, int yPosition, Color fishColor)
        {
            // If no letter specified, use "X."
            if (fishLetter.Length == 0)
                fishLetter = "X";
            _fishLetter = fishLetter;
            
            // Ensure the position is >= 0.
            if (xPosition < 0)
                xPosition = 0;
            _xPosition = xPosition;
            
            if (yPosition < 0)
                yPosition = 0;
            _yPosition = yPosition;
            
            // Set the fish color.
            _fishColor = fishColor;
        }
    }
}

The Fish class embodies the properties and methods (just the Fish() constructor for now) we need to create and track a fish in our virtual aquarium. Essentially, we include the color of the fish (_fishColor), the letter representing the fish (_fishLetter), and the position of the fish (_xPosition, _yPosition). Each of these properties, as class variables, is accessed using an accessor, and implemented using the appropriate get and set statements. The Fish() constructor includes parameters to initialize the properties of the fish, code that tests the values of the parameters and sets them to default values if necessary, and the code to set the class variables from the Fish() parameters. You'll also note that we used a using statement to include the System.Drawing namespace for access to the Color type.

Let's now add a class to represent our fish tank.

Right-click on the AlphabetAquarium project item in the Solution Explorer, select Add, and then select Class.... In the Add New Item dialog box, change the Name from Class1.cs to FishTank.cs, then click Add. In the FishTank.cs Code Editor, modify the code as shown:

FishTank.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace AlphabetAquarium
{
    class FishTank
    {
        // Use a List collection to hold the fish.
        private List<Fish> _fishTank = new List<Fish>();
        
        public int CountFish()
        {
            return _fishTank.Count;
        }
        
        public Fish GetFish(int position)
        {
            return _fishTank[position]; 
        }
        
        public void AddFish(Fish fish)
        {
            _fishTank.Add(fish);
        }
        
        public void ClearFish()
        {
            _fishTank.Clear();
        }
    }
}

Click to save your changes. We can't run it yet, but let's discuss this code.

OBSERVE: FishTank.cs
.
.
.
namespace AlphabetAquarium
{
    class FishTank
    {
        // Use a List collection to hold the fish.
        private List<Fish> _fishTank = new List<Fish>();
        
        public int CountFish()
        {
            return _fishTank.Count;
        }
        
        public Fish GetFish(int position)
        {
            return _fishTank[position]; 
        }
        
        public void AddFish(Fish fish)
        {
            _fishTank.Add(fish);
        }
        
        public void ClearFish()
        {
            _fishTank.Clear();
        }
    }
}

This code uses a C# collection List object, and the correct C# syntax appropriate to a collection (we'll cover these collection objects more completely later). We create a List object variable, _fishTank, using the new operator, that will hold each instance of the Fish class we created previously, effectively representing our aquarium. We use List methods to add a fish to our tank, remove all fish from our tank, return the number of fish in our tank, and to return a reference to specific fish instance.

Finally, we need to add the code to use the Fish and FishTank classes, enable all of the event handlers for our controls, and add methods and code to initialize and draw our fish.

Let's start by adding the event handlers for our ComboBox and Button controls. However, in the past, we typically double-clicked a control to generate the code for an event handler. This method works great when the event you want handled is the default event that's generated when you double-click a control, but sometimes that isn't the event you want handled. So, for the events we need for this application, we'll use the Events lightning bolt icon of the Properties Window.

TipRemember that the Properties Window, which includes the Events icon, is sensitive to what you have selected, so make sure you select the correct control or Form in creating event handlers. You can create an event handler by double-clicking on the event. The name of the method to handle the event will automatically populate the ComboBox next to the event, and you will be taken to the Code Editor, with the cursor positioned within the new event handler. If you accidentally click the wrong event, or decide later you do not want that event handler, you cannot simply delete the event code in the Code Editor. You must return to the Events Property page, and delete the event handler method name in the ComboBox next to the event. If you have not modified the event handler code in the Code Editor, then deleting the event handler method name in the Events Property page will also delete the event code from the Code Editor.

Select the AlphabetAquarium.cs [Design] Form Editor, and for each control or Form listed in the table below, select the Events icon in the Properties Window and create the appropriate Event handler by double-clicking the event. You will see the generated code in the Code Editor after each event handler is generated, so you'll need to return to the AlphabetAquarium.cs [Design] Form Editor to select the next control. When you finish, click to save your changes.

Control/FormEventEvent Handler
AlphabetAquarium FormLoadAlphabetAquarium_Load
colorsComboBoxSelectedIndexChangedcolorsComboBox_SelectedIndexChanged
addFishButtonClickaddFishButton_Click
clearFishButtonClickclearFishButton_Click
exitButtonClickexitButton_Click
fishTankPanel (the left panel)PaintfishTankPanel_Paint

TipIf you need to select a control but can't find it, you can always select the control from the ComboBox at the top of the Properties Window.

The Code Editor should now have all of the event handlers that we'll need for this project, but before we add the code, let's discuss how the program is going to work.

Control/FormEventDiscussion
AlphabetAquarium FormLoadPopulate the colorsComboBox with all available "named" colors, and select Black as the default. Populate the fishComboBox with all of the letters of the alphabet, and select the first entry in the ComboBox.
colorsComboBoxSelectedIndexChangedChange the color of the colorPictureBox to match the selected color.
addFishButtonClickAdd the selected "fish" letter using the selected color to a fish tank Collection, generating a random location on the fishTankPanel. Generate an event to make the fishTankPanel update the display of our fish from the fish tank Collection.
clearFishButtonClickClear the fish tank Collection. Generate an event to make the fishTankPanel update the display of our fish from the fish tank Collection.
exitButtonClickClose the application.
fishTankPanelPaintFor each fish stored in our fish tank Collection, get the letter, color, and location, and draw the "fish" on the fishTankPanel.

As you can see, we've tried to apply our "model" concept as we created our classes to represent our aquarium, and as we created events to handle the interaction with our fish and our fish tank. Using concepts that model the real world as closely as possible makes understanding our code easier. So, let's add code to populate our ComboBox controls, display the selected color for each "fish" we add, set a class variable to store our added fish, and exit the application.

Modify the AquariumAlphabet.cs code as shown below.

AquariumAlphabet.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 AlphabetAquarium
{
    public partial class AlphabetAquarium : Form
    {
        private FishTank _fishTank = new FishTank();
    
        public AlphabetAquarium()
        {
            InitializeComponent();
        }
        
        private void AlphabetAquarium_Load(object sender, EventArgs e)
        {
            // Populate "colors" ComboBox, select "Black" as default.
            colorsComboBox.Items.Add("Black");
            colorsComboBox.Items.Add("Red");
            colorsComboBox.Items.Add("Green");
            colorsComboBox.Items.Add("Blue");
            colorsComboBox.SelectedIndex = colorsComboBox.FindString("Black");
            // Populate the "fish" ComboBox, select "A" as default
            fishComboBox.Items.Add("A");
            fishComboBox.Items.Add("B");
            fishComboBox.Items.Add("C");
            fishComboBox.Items.Add("D");
            fishComboBox.SelectedIndex = fishComboBox.FindString("A");
        }
        
        private void colorsComboBox_SelectedIndexChanged(object sender, EventArgs e)
        {
            // Change the color of the colorPictureBox to match the selected color.
            colorPictureBox.BackColor = Color.FromName(colorsComboBox.SelectedItem.ToString());
        }
        
        private void addFishButton_Click(object sender, EventArgs e)
        {
        }
        
        private void fishTankPanel_Paint(object sender, PaintEventArgs e)
        {
        }
        
        private void clearFishButton_Click(object sender, EventArgs e)
        {
            _fishTank.ClearFish();
            fishTankPanel.Invalidate();
        }
        
        private void exitButton_Click(object sender, EventArgs e)
        {
            this.Close();
        }
    }
}

, and to run the program. Select a different color and note that the color picture box changes. Any new fish added will have this color.

Let's discuss how it works.

OBSERVE: AquariumAlphabet.cs
.
.
.

namespace AlphabetAquarium
{
    public partial class AlphabetAquarium : Form
    {
        private FishTank _fishTank = new FishTank();
        
        public AlphabetAquarium()
        {
            InitializeComponent();
        }
        
        private void AlphabetAquarium_Load(object sender, EventArgs e)
        {
            // Populate "colors" ComboBox, select "Black" as default.
            colorsComboBox.Items.Add("Black");
            colorsComboBox.Items.Add("Red");
            colorsComboBox.Items.Add("Green");
            colorsComboBox.Items.Add("Blue");
            colorsComboBox.SelectedIndex = colorsComboBox.FindString("Black");
            // Populate the "fish" ComboBox, select "A" as default
            fishComboBox.Items.Add("A");
            fishComboBox.Items.Add("B");
            fishComboBox.Items.Add("C");
            fishComboBox.Items.Add("D");
            fishComboBox.SelectedIndex = fishComboBox.FindString("A");
        }
        
        private void colorsComboBox_SelectedIndexChanged(object sender, EventArgs e)
        {
            // Change the color of the colorPictureBox to match the selected color.
            colorPictureBox.BackColor = Color.FromNamecolorsComboBox.SelectedItem.ToString());
        }
.
.
.

With this code, we use the ComboBox Add() method of the Items collection that holds the ComboBox data to populate both the colorsComboBox and fishComboBox. We then set the SelectedIndex property of the ComboBox by locating an appropriate default value for each ComboBox using the FindString method. As a color is selected in the colorsComboBox, the colorsComboBox_SelectedIndexChanged event is called, or "raised," and we use the Color.FromName() method to select the Color based on the name of the color, setting the BackColor of the colorPictureBox control.

We've also added a _fishTank class variable of type FishTank that will hold our "fish." The _fishTank instance class variable of the FishTank class gives us access to all of the methods of this class, and represents our aquarium in our model.

OBSERVE: Clearing the Fish
.
.
.        
    private void clearFishButton_Click(object sender, EventArgs e)
        {
            _fishTank.ClearFish();
            fishTankPanel.Invalidate();
        }       
.
.
.

When the clearFishButton is clicked, the clearFishButton_Click event is raised, and we call the ClearFish() method. We then call the Invalidate() method of the fishTankPanel control. The Invalidate() method forces the fishTankPanel to redraw itself. When a control is drawn, the Paint event is called (fishTankPanel_Paint in our code), so let's proceed to implement the remaining code to complete our aquarium.

Modify the AquariumAlphabet.cs code as shown below.

AquariumAlphabet.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 AlphabetAquarium
{
    public partial class AlphabetAquarium : Form
    {
        private FishTank _fishTank = new FishTank();
        
        public AlphabetAquarium()
        {
            InitializeComponent();
        }
        
        private void AlphabetAquarium_Load(object sender, EventArgs e)
        {
            // Populate "colors" ComboBox, select "Black" as default.
            colorsComboBox.Items.Add("Black");
            colorsComboBox.Items.Add("Red");
            colorsComboBox.Items.Add("Green");
            colorsComboBox.Items.Add("Blue");
            colorsComboBox.SelectedIndex = colorsComboBox.FindString("Black");
            // Populate the "fish" ComboBox, select "A" as default.
            fishComboBox.Items.Add("A");
            fishComboBox.Items.Add("B");
            fishComboBox.Items.Add("C");
            fishComboBox.Items.Add("D");
            fishComboBox.SelectedIndex = fishComboBox.FindString("A");
        }
        
        private void colorsComboBox_SelectedIndexChanged(object sender, EventArgs e)
        {
            // Change the color of the colorPictureBox to match the selected color.
            colorPictureBox.BackColor = Color.FromName(colorsComboBox.SelectedItem.ToString());
        }
        
        private void addFishButton_Click(object sender, EventArgs e)
        {
            // Use the boundaries of the fishTankPanel to limit our random x, y location.
            Rectangle fishTankRect = fishTankPanel.Bounds;
            Random random = new Random();
            int x = random.Next(10, fishTankRect.Width - 10);
            int y = random.Next(10, fishTankRect.Height - 10);
            // Create a new Fish object, and add to our fish tank.
            Fish fish = new Fish(fishComboBox.SelectedItem.ToString(), x, y, Color.FromName(colorsComboBox.SelectedItem.ToString()));
            _fishTank.AddFish(fish);
            fishTankPanel.Invalidate();
        }
        
        private void fishTankPanel_Paint(object sender, PaintEventArgs e)
        {
            // Loop through each fish in our fish tank, and draw them.
            for (int i = 0; i < _fishTank.CountFish(); i++)
            {
                Fish fish = _fishTank.GetFish(i);
                e.Graphics.DrawString(fish.FishLetter, new Font("Arial", 10),
                    new SolidBrush(fish.FishColor), new Point(fish.XPosition, fish.YPosition));
            }
            fishCountLabel.Text = _fishTank.CountFish().ToString();
        }
        
        private void clearFishButton_Click(object sender, EventArgs e)
        {
            _fishTank.ClearFish();
            fishTankPanel.Invalidate();
        }
        
        private void exitButton_Click(object sender, EventArgs e)
        {
            this.Close();
        }
    }
}

and to run the program. Now you can add fish of different colors and have them appear in random locations in the fish tank.

Let's discuss how this code works.

OBSERVE: AquariumAlphabet.cs
.
.
.
        private void addFishButton_Click(object sender, EventArgs e)
        {
            // Use the boundaries of the fishTankPanel to limit our random x, y location.
            Rectangle fishTankRect = fishTankPanel.Bounds;
            Random random = new Random();
            int x = random.Next(10, fishTankRect.Width - 10);
            int y = random.Next(10, fishTankRect.Height - 10);
            // Create a new Fish object, and add to our fish tank.
            Fish fish = new Fish(fishComboBox.SelectedItem.ToString(), x, y, Color.FromName(colorsComboBox.SelectedItem.ToString()));
            _fishTank.AddFish(fish);
            fishTankPanel.Invalidate();
        }
        
        private void fishTankPanel_Paint(object sender, PaintEventArgs e)
        {
            // Loop through each fish in our fish tank, and draw them.
            for (int i = 0; i < _fishTank.CountFish(); i++)
            {
                Fish fish = _fishTank.GetFish(i);
                e.Graphics.DrawString(fish.FishLetter, new Font("Arial", 10),
                    new SolidBrush(fish.FishColor), new Point(fish.XPosition, fish.YPosition));
            }
            fishCountLabel.Text = _fishTank.CountFish().ToString();
        }
.
.
.        

This code completes everything we need to fully implement our virtual alphabet "fish" aquarium! We added code to the addFishButton_Click event handler to generate a random location using the Random class. To make sure our random location falls within the limits of the fishTankPanel, we call the Bounds method of the fishTankPanel to return a Rectangle object, setting the fishTankRect variable. We use this variable later when we generate the actual random number for our location. We next create a Fish instance, fish, passing in the necessary parameters to the Fish constructor, which includes our fish letter, the location of the fish, and the Color. We add the new fish to our _fishTank, and then force the redraw, or repaint of the fishTankPanel by calling the Invalidate() method.

The fishTankPanel_Paint method is the event handler that's raised whenever the fishTankPanel control needs to be redrawn, which we force when we call Invalidate() method. We've added a for loop to the fishTankPanel_Paint method, looping through the _fishTank object that contains all of our "fish." We limit the for loop using the _fishTank.CountFish() method, extracting each fish by calling the _fishTank.GetFish method, passing the loop variable i. We use the DrawString method of the e Graphics object to draw our fish letter, passing in the necessary parameters. We use the FishLetter, FishColor, XPosition and YPosition methods of the fish object to extract the fish information. We also create a Font, SolidBrush, and Point object "on-the-fly" (without setting a variable) to complete the parameters needed by DrawString. Then, finally, we call the CountFish() method of the _fishTank to set the fish count in our GUI.

Classes: A New Data Type

When you create a class, you're creating a new data type. You can see this fact with the declaration of our _fishTank class variable: private FishTank _fishTank. Why is this fact important, besides the obvious about how you use a class? Earlier, we discussed the concept of our programming code representing, or modeling, the real world, and as you continue to learn more C#, and just as importantly, object-oriented programming concepts, you'll need to remember the flexibility and versatility of being able to create your own data types to find solutions.

Objects: A Multiplicity of Instances

Again related to our modeling the real world, even though we have a class blueprint, we also take advantage of the ability to create an instance of a class, creating a class instance object. For our aquarium, each time a "fish" was added, we created an instance of our Fish class, filling our FishTank object with multiple instances of Fish, just like you'd have in the real world.

Before you move on to the next lesson, do your homework! Right-click in the window where this lesson text appears and select Back. Then select Quiz for this lesson in the syllabus and answer the quiz questions. When you finish the quiz questions, click HAND IT IN at the bottom of that window. Then do the same with the Project(s) for the lesson. Your instructor will grade your quiz(zes) and project(s) and provide guidance if needed.