Classes and Objects
In the Object Orientation chapter the conceptual topics inheritance, polymorphism, and encapsulation were introduced. Every OO language has its own variation of object orientation with some syntactic differences. Not all OO languages share the same features. However, the majority of the concepts and features can be found in the most popular OO languages. In this and subsequent chapters, we will explore how C# implements object orientation. We use the approach of developing a number of classes to demonstrate the OO topics.
To get started, you can either create a new solution or add the ClassesAndObjects project to the ITSE1330Solution-1. Code Program.cs as shown. Notice the ClassesAndObjects namespace on line 7. The other three classes we add to this project will also use the ClassesAndObjects namespace. Even though we specify the namespace in the other class files it is not required since ClassesAndObjects is the default namespace for the assembly. However, VS automatically includes the namespace directive and we will retain it for clarity. The default namespace of a project can be verified by R-clicking the project name | Properties | Application as shown.
The Rectangle class is specified on lines 9 - 41. Recall that a class is just a blueprint and that no memory is allocated until an object is instantiated from the class. Variables, properties, and methods are all known as members of the class. Lines 11 and 12 contain the instance variables length and width. They are known as instance variables since every object (instance) created from the class gets its own copy of the variable. On the other hand, member variables can also be shared by all objects of the class by using the static keyword which we discuss in the next chapter.
Notice the keyword private on lines 11 and 12. It is known as a "member access specifier". This means that length and width are "private" to the class and can only be accessed by other members of the class. This is an example of information hiding and encapsulation in which access to class members are provided on a "need to know" basis. It is common practice to designate member data (variables) as private and member methods as public. Note that all member methods of the class (e.g. lines 14, 18, 25) are set to public. There is another access specifier (protected) but that is beyond the scope of this introductory course.
In Program.cs, lines 14-17 and 18-24 designate special member methods known as constructors. A constructor method is automatically called when a object of the class is created. Rectangle objects are created on lines 46 and 47 and a constructor is called when each is created. However, since line 46 includes no arguments and line 47 includes arguments, different constructors are called when objects rectangle1 and rectangle2 are created. Class constructor methods must be public and have the same name of the class. So, both constructors are public and have the name Rectangle which is the same name as the class.
The constructor on lines 14-17 is a PDDC (see table). It is programmer-defined since we write it and it is a default constructor since it has no parameters. A default constructor is called when an object is created that does not have arguments in the constructor call. C# includes a hidden default constructor (DC) if no constructors are supplied by the programmer. However, if a non-default constructor is supplied (PDC), then C# requires that a PDDC also be supplied. You are encouraged to test this point by including a PDC without a PDDC.
|DC||Default Constructor||System supplied (hidden)|
|PDDC||Programmer-Defined Default Constructor||Written by programmer|
|PDC||Programmer-Defined Constructor||Written by programmer|
Line 46 is an example of a Rectangle object being created that does not include arguments. That is, the parentheses after Rectangle are empty. Since no arguments are supplied, a default constructor is called. When line 46 executes, the compiler looks for a matching constructor (one that has no arguments) and finds it on lines 14-17. We can see the result of line 46 in the output where the WriteLine statement is written to the command window.
Lines 18-24 specify a PDC (see table) that will be called when two doubles are supplied in the argument list which occurs on line 47. An object of type Rectangle, named rectangle2 is created on line 47. Two arguments ( 10, 20) are supplied in the constructor call. The constructor that matches that signature (a parameter list of two doubles) is located on lines 18-24. Recall from the Object Orientation chapter that this is an example of polymorphism which enables methods of the same name to exist but must contain different parameter lists. The CLR determines which method to run by matching the argument list in the method call (line 47) with the parameter list in the method header (line 18).
The next four methods of the class are known as accessors (getters) and mutators (setters). When we work with properties below, we use the term accessors to refer to both getters and setters. But, when working with separate methods like those in the Rectangle class, we use the more formal terms accessors and mutators to distinguish between getters and setters. Many classes have these two types of methods that are used to work with class data. Getters are used to return object data to the calling location and setters are used to set object data. The getters on lines 25 and 33 return the values of length and width that are stored in the object. The setters on lines 29 and 37 set the values of length and width when those methods are called. The parameter in SetLength() is named paramLength to distinguish it from the member data variable named length. We can also use the keyword "this" to accomplish the same thing which we do in the Circle class below.
Lines 42-149 comprise the Program class which includes the Main() method of the program and is thus the main class. On lines 46-52 seven objects are created. The type of each object is specified by the class and constructor names used. For instance, on line 50 an object of type Triangle is created with the name triangle1. Notice that on line 50 the default constructor of the Triangle class is called since no arguments are supplied. PDCs are called on lines 47, 49, and 51 since each of those calls contain arguments.
Notice that in addition to Program.cs below, there are three other files for the Circle, Triangle, and Cone classes. Unlike the Rectangle class which was declared in Program.cs on lines 9-41, the Circle, Triangle, and Cone classes are each declared in separate files. To declare a class in a separate file R-click on the project name in Solution Explorer | Add | New Item | Class and name the class file accordingly.
All of the constructors in these classes are similar to those reviewed for the Rectangle class. However, there are a few notable differences. Notice on line 18 of the Circle class that the keyword "this" is used which is a reference to the current object. The technique used here is to differentiate the parameter radius (which is a variable local to the method) to the instance variable radius which is associated with the object.
The "this" reference on line 18 refers to the instance variable while radius (on the right side of the assignment operator (=)) is the parameter that was passed when the method was called (line 49 of the Program.cs file). Some developers recommend using "this" when accessing all instance variables since it enhances code readability. That technique is also used in the PDC in Triangle.cs on lines 19 and 20. The this keyword cannot be used in a static method since those methods reside at the class level and therefore no object is associated with it. More on static methods in the next chapter.
There is another difference in the class constructors. Notice that the Cone class in Cone.cs does not have a constructor listed. When a constructor is not supplied by the programmer, the compiler provides a hidden default constructor (DC). The creation of a Cone object on line 52 of Program.cs uses the hidden DC since no arguments are supplied. Recall that if a PDC is used, a PDDC must also be written since the compiler no longer supplies the DC.
Using Member Methods and Member Data
On lines 54-60 of Program.cs seven double variables are declared and defined. Note that the variables have meaningful names. For instance, triHeight is used to store the height of a triangle. This is considered a "best practice" and should be employed. However, there are times when small identifiers are suitable like as i, j, or k such as iteration control variables or others with limited scope.
For the most part, from this point forward in the course, concepts in the code that have already been covered will not be reviewed. For instance, there is nothing new on lines 64-67, 87-90, etc. On line 69, a mutator (setter) member method SetLength() of the Rectangle class is called (a.k.a. invoked). The variable recLength that was obtained from the user is passed as an argument to the method. When items (variables or literals) are included in a method call like on line 69, the items are known as arguments.
On the other hand, line 29 is the method header and the items in parentheses are known as parameters. Arguments are passed from the method call to the appropriate parameter in the method declaration. The SetLength() method is declared on lines 29-32. The recLength variable is passed to the method and is assigned to the instance variable length on line 31. Analogous steps are performed with the call to SetWidth() on line 73.
On lines 75 and 76 the accessor (getter) methods are called and return the length and width values of the rectangle1 object. The accessor methods of rectangle2 are called on lines 82 and 83. Unlike with rectangle1, the mutator (setter) methods were not called for the rectangle2 object. How then are the length and width variables set for rectangle2? See line 47 for a hint.
The section of code from lines 87-102 works with the circle1 and circle2 objects. Most of the steps are similar to that covered with the rectangle objects. The notable difference is the calls to the CalcArea() member method on lines 95 and 102. The CalcArea() method is declared on lines 30-35 in the Circle.cs file. It is neither an accessor or mutator but rather a member method dedicated to calculating and returning the area of the circle object based on the radius supplied by the user and set by the SetRadius() mutator.
The section of code from lines 106-127 works with the triangle1 and triangle2 objects. The Triangle class uses a new technique known as a property. Properties combine the getter and setter operations in one data type. In Triangle.cs, see the instance member variables tbase and height on lines 11 and 12. The properties associated with the instance variables are on lines 24 and 37. Tbase is the property for tbase and Height is the property for height. It is a naming convention that properties use the upper camel case version of the instance variable name.
Now, back to Program.cs to see the properties in action. Note the use of Tbase and Height on lines 111 and 115. Only the property name is used and the compiler calls the set accessor within the property since the assignment operator is used. Recall with the Rectangle and Circle class the setter methods were called. With properties, the property name is used and the compiler determines whether to call the set or get accessors. Note that the get accessors of the properties are called on lines 117, 118, 125, and 126. Take a look at the properties again in Circle.cs. Notice the get and set accessors.
In the last section of code in Program.cs the Cone class is used. Recall that a constructor is not specified in the Cone class but rather the DC is used. Like the Triangle class, the Cone class also uses properties. On lines 136 and 140 set accessors of properties are called. And, on lines 142 and 143 the get accessors are called. The new technique featured in the Cone class is located on lines 11 and 12 of Cone.cs. ConeRadius and ConeHeight are known as "auto-implemented properties" (a.k.a auto properties). They have the same capabilities as the properties located in the Triangle and Circle classes but are much more compact. Not only are the get and set accessors provided, it is not necessary to specify instance members associated with the properties. With auto properties, private fields (instance variables) are created and can only be accessed via the auto property get and set accessors.
In the next chapter, we discuss static methods, static fields, and method parameters.