C H A P T E R  4

Classes: The Basics

Overview of Classes

In the previous chapter, you saw that C# provides six user-defined types. The most important of these, and the one I’ll cover first, is the class. Since the topic of classes in C# is a large one, I’ll spread its discussion over the next several chapters.

A Class Is an Active Data Structure

Before the days of object-oriented analysis and design, programmers thought of a program as just a sequence of instructions. The focus at that time was on structuring and optimizing those instructions. With the advent of the object-oriented paradigm, the focus changed from optimizing instructions to organizing a program’s data and functions into encapsulated sets of logically related data items and functions, called classes.

A class is a data structure that can store data and execute code. It contains data members and function members:

  • Data members store data associated with the class or an instance of the class. Data members generally model the attributes of the real-world object the class represents.
  • Function members execute code. These generally model the functions and actions of the real-world object the class represents.

A C# class can have any number of data and function members. The members can be any combination of nine possible member types. Table 4-1 shows these member types. In this chapter I’ll cover fields and methods.

Image

Image Note Classes are encapsulated sets of logically related data items and functions that generally represent objects in the real world or a conceptual world.

Programs and Classes: A Quick Example

A running C# program is a group of interacting type objects, most of which are instances of classes. For example, suppose you have a program simulating a poker game. When it’s running, it might have an instance of a class called Dealer, whose job is to run the game, and several instances of a class called Player, which represent the players of the game.

The Dealer object stores such information as the current state of the card deck and the number of players. Its actions include shuffling the deck and dealing the cards.

The Player class is very different. It stores such information as the player’s name and the amount of money left to bet, and it performs actions such as analyzing the player’s current hand and placing bets. Figure 4-1 illustrates the running program. The class names are shown outside the boxes, and the instance names are inside.

Image

Figure 4-1. The objects in a running program

A real program would undoubtedly contain dozens of other classes besides Dealer and Player. These would include classes such as Card and Deck. Each class models some thing that is a component of the poker game.

Image Note  A running program is a set of objects interacting with each other.

Declaring a Class

Although types int, double, and char are defined in the C# language, classes such as Dealer and Player, as you can probably guess, are not defined by the language. If you want to use them in a program, you’ll have to define them yourself. You do this by writing a class declaration.

A class declaration defines the characteristics and members of a new class. It does not create an instance of the class but creates the template from which class instances will be created. The class declaration provides the following:

  • The class name
  • The members of the class
  • The characteristics of the class

The following is an example of the minimum syntax for a class declaration. The curly braces contain the member declarations that make up the class body. Class members can be declared in any order inside the class body. This means it’s perfectly fine for the declaration of a member to refer to another member that is not yet defined until further down in the class declaration.

    Keyword     Class name
                 
     class MyExcellentClass
     {
        MemberDeclarations
     }

The following code shows the outlines of two class declarations:

   class Dealer          // Class declaration
   {
      ...
   }

   class Player          // Class declaration
   {
      ...
   }

Image Note  Since a class declaration “defines” a new class, you will often see a class declaration referred to as a class definition both in the literature and in common usage among programmers.

Class Members

Two of the most important class member types are fields and methods. Fields are data members, and methods are function members.

Fields

A field is a variable that belongs to a class.

  • It can be of any type, either predefined or user-defined.
  • Like all variables, fields store data and have the following characteristics:
    • They can be written to.
    • They can be read from.

The minimum syntax for declaring a field is the following:

   Type
     
   Type Identifier;
            
         Field name

For example, the following class contains the declaration of field MyField, which can store an int value:

   class MyClass
   {  Type
        
       int MyField;
               
   }       Field name

Image Note  Unlike C and C++, in C# there are no global variables (that is, variables or fields) declared outside of a type. All fields belong to a type and must be declared within their type declaration.

Explicit and Implicit Field Initialization

Since a field is a kind of variable, the syntax for a field initializer is the same as that of the variable initializer shown in the previous chapter.

  • A field initializer is part of the field declaration and consists of an equal sign followed by an expression that evaluates to a value.
  • The initialization value must be determinable at compile time.
       class MyClass
       {
          int F1 = 17;
       }           
              Field initializer
  • If no initializer is used, the compiler sets the value of a field to a default value, determined by the type of the field. Table 3-1 (in Chapter 3) gives the default values for the simple types. To summarize them: The default value for each type is 0, and false for bool. The default for reference types is null.

For example, the following code declares four fields. The first two fields are initialized implicitly. The second two fields are initialized explicitly with initializers.

   class MyClass
   {
       int    F1;                     // Initialized to 0    - value type
       string F2;                     // Initialized to null - reference type

       int    F3 = 25;                // Initialized to 25
       string F4 = "abcd";            // Initialized to "abcd"
   }
Declarations with Multiple Fields

You can declare multiple fields of the same type in the same statement by separating the names with commas. You cannot mix different types in a single declaration. For example, you can combine the four preceding field declarations into two statements, with the exact same semantic result:

   int    F1, F3 = 25;
   string F2, F4 = "abcd";

Methods

A method is a named block of executable code that can be executed from many different parts of the program, and even from other programs. (There are also anonymous methods, which aren’t named—but I’ll cover those in Chapter 13.)

When a method is called, or invoked, it executes the method’s code, and then returns to the code that called it and continues executing the calling code. Some methods return a value to the position from which they were called. Methods correspond to member functions in C++.

The minimum syntax for declaring a method includes the following components:

  • Return type: This states the type of value the method returns. If a method doesn’t return a value, the return type is specified as void.
  • Name: This is the name of the method.
  • Parameter list: This consists of at least an empty set of matching parentheses. If there are parameters (which I’ll cover in the next chapter), they’re listed between the parentheses.
  • Method body: This consists of a matching set of curly braces containing the executable code.

For example, the following code declares a class with a simple method called PrintNums. From the declaration, you can tell the following about PrintNums:

  • The return type is specified as void, hence it returns no value.
  • It has an empty parameter list.
  • It contains two lines of code in its method body. The first prints out the number 1 and the second prints out 2.
   class SimpleClass
   {
    Return type    Parameter list
                   
      void PrintNums( )
      {
         Console.WriteLine("1");
         Console.WriteLine("2");
      }
   }

Image Note  Unlike C and C++, in C# there are no global functions (that is, methods or functions) declared outside of a type declaration. Also unlike C and C++, in C# there is no “default” return type for a method. All methods must include a return type or list it as void.

Creating Variables and Instances of a Class

The class declaration is just the blueprint from which instances of the class are created. Once a class is declared, you can create instances of the class.

  • Classes are reference types, which, as you will remember from the previous chapter, means that they require memory both for the reference to the data and for the actual data.
  • The reference to the data is stored in a variable of the class type. So, to create an instance of the class, you need to start by declaring a variable of the class type. If the variable isn’t initialized, its value is undefined.

Figure 4-2 illustrates how to define the variable to hold the reference. At the top of the code on the left is a declaration for class Dealer. Below that is a declaration for class Program, which contains method Main. Main declares variable theDealer of type Dealer. Since the variable is uninitialized, its value is undefined, as shown on the right in the figure.

Image

Figure 4-2. Allocating memory for the reference of a class variable

Allocating Memory for the Data

Declaring the variable of the class type allocates the memory to hold the reference, but not the memory to hold the actual data of the class object. To allocate memory for the actual data, you use the new operator.

  • The new operator allocates and initializes memory for an instance of the specified type. It allocates the memory from either the stack or the heap, depending on the type.
  • Use the new operator to form an object-creation expression, which consists of the following:
    • The keyword new.
    • The name of the type of the instance for which memory is to be allocated.
    • Matching parentheses, which might or might not include parameters. I’ll discuss more about the possible parameters later.
Keyword   Parentheses are required.
                 
  new TypeName ( )
         
       Type
  • If the memory allocated is for a reference type, the object-creation expression returns a reference to the allocated and initialized instance of the object in the heap.

This is exactly what you need to allocate and initialize the memory to hold the class instance data. Use the new operator to create an object-creation expression, and assign the value returned by it to the class variable. Here’s an example:

   Dealer theDealer;             // Declare variable for the reference
   theDealer = new Dealer();     // Allocate memory for the class object and assign
                                // it to the variable
             Object-creation expression

The code on the left in Figure 4-3 shows the new operator used to allocate memory and create an instance of class Dealer, which is then assigned to the class variable. The memory structure is illustrated in the figure, to the right of the code.

Image

Figure 4-3. Allocating memory for the data of a class variable

Combining the Steps

You can combine the two steps by initializing the variable with the object-creation expression.

        Declare variable
              ↓       
       Dealer theDealer = new Dealer();                // Declare and initialize
                               
                Initialize with an object-creation expression.

Instance Members

A class declaration acts as a blueprint from which you can create as many instances of the class as you like.

  • Instance members: Each instance of a class is a separate entity that has its own set of data members, distinct from the other instances of the same class. These are called instance members since they are associated with an instance of the class.
  • Static members: Instance members are the default, but you can also declare members called static members, which are associated with the class, not the instance. I’ll cover these in Chapter 6.

As an example of instance members, the following code shows the poker program with three instances of class Player. Figure 4-4 shows that each instance has a different value for the Name field.

   class Dealer { ... }                            // Declare class
   class Player {                                  // Declare class
      string Name;                                 // Field
         ...
   }

   class Program {
      static void Main()
      {
         Dealer theDealer = new Dealer();
         Player player1   = new Player();
         Player player2   = new Player();
         Player player3   = new Player();
         ...
      }
   }
Image

Figure 4-4. Instance members have distinct values between class objects.

Access Modifiers

From within a class, any function member can access any other member of the class by simply using that member’s name.

The access modifier is an optional part of a member declaration that specifies what other parts of the program have access to the member. The access modifier is placed before the simple declaration forms. The following is the syntax for fields and methods:

  Fields
     AccessModifier Type Identifier

  Methods
     AccessModifier ReturnType MethodName ()
     {
        ...
     }

The five categories of member access are the following. I’ll describe the first two in this chapter and the others in Chapter 7.

  • private
  • public
  • protected
  • internal
  • protected internal

Private and Public Access

Private members are accessible only from within the class in which they are declared—other classes cannot see or access them.

  • Private access is the default access level, so if a member is declared without an access modifier, it is a private member.
  • You can also use the private access modifier to explicitly declare a member as private. There’s no semantic difference between declaring a private member implicitly as opposed to explicitly. The forms are equivalent.

For example, the following two declarations both specify private int members:

           int MyInt1;                 // Implicitly declared private

   private int MyInt2;                 // Explicitly declared private
      
 Access modifier

Public members of an instance are accessible to other objects in the program. You must use the public access modifier to specify public access.

    Access modifier
        
      public int MyInt;
Depicting Public and Private Access

The figures in this text represent classes as labeled boxes, as shown in Figure 4-5.

  • The class members are represented as smaller labeled boxes inside the class boxes.
  • Private members are represented as enclosed entirely within their class box.
  • Public members are represented as sticking partially outside their class box.
Image

Figure 4-5. Representing classes and members

Example of Member Access

Class C1 in the following code declares both public and private fields and methods. Figure 4-6 illustrates the visibility of the members of class C1.

   class C1
   {
      int         F1;                     // Implicit private field
      private int F2;                     // Explicit private field
      public  int F3;                     // Public field

      void DoCalc()                       // Implicit private method
      {
         ...
      }

      public int GetVal()                 // Public method
      {
         ...
      }
   }
Image

Figure 4-6. Private and public class members

Accessing Members from Inside the Class

As mentioned, members of a class can access the other class members by just using their names.

For example, the following class declaration shows the methods of the class accessing the fields and other methods. Even though the fields and two of the methods are declared private, all the members of a class can be accessed by any method (or any function member) of the class. Figure 4-7 illustrates the code.

   class DaysTemp
   {
      // Fields
      private int High = 75;
      private int Low  = 45;

      // Methods
      private int GetHigh()
      {
         return High;                          // Access private field
      }

      private int GetLow()
      {
         return Low;                           // Access private field
      }

      public float Average ()
      {
         return (GetHigh() + GetLow()) / 2;    // Access private methods
      }                       
   }            Accessing the private methods
Image

Figure 4-7. Members within a class can freely access each other.

Accessing Members from Outside the Class

To access a public instance member from outside the class, you must include the variable name and the member name, separated by a period (dot). This is called dot-syntax notation; I’ll describe it in more detail later.

For example, the second line of the following code shows an example of accessing a method from outside the class:

   DaysTemp myDt = new DaysTemp();      // Create an object of the class
   float fValue  = myDt.Average();      // Access it from outside
                          
             Variable name  Member name

As an example, the following code declares two classes: DaysTemp and Program.

  • The two fields in DaysTemp are declared public, so they can be accessed from outside the class.
  • Method Main is a member of class Program. It creates a variable and object of class DaysTemp, and it assigns values to the fields of the object. It then reads the values of the fields and prints them out.
   class DaysTemp                                   // Declare class DaysTemp
   {
      public int High = 75;
      public int Low  = 45;
   }

   class Program                                    // Declare class Program
   {
      static void Main()
      {       Variable name
                   
         DaysTemp temp = new DaysTemp();            // Create the object
   Variable name and field
                 
         temp.High = 85;                            // Assign to the fields
         temp.Low  = 60;               Variable name and field
                                                   
         Console.WriteLine("High:   {0}", temp.High );     // Read from fields
         Console.WriteLine("Low:    {0}", temp.Low  );
      }
   }

This code produces the following output:


High:  85
Low:   60

Putting It All Together

The following code creates two instances and stores their references in variables named t1 and t2. Figure 4-8 illustrates t1 and t2 in memory. The code demonstrates the following three actions discussed so far in the use of a class:

  • Declaring a class
  • Creating instances of the class
  • Accessing the class members (that is, writing to a field and reading from a field)
   class DaysTemp                          // Declare the class
   {
      public int High, Low;                // Declare the instance fields
      public int Average()                 // Declare the instance method
      {
         return (High + Low) / 2;
      }
   }

   class Program
   {
      static void Main()
      {
         // Create two instances of DaysTemp
         DaysTemp t1 = new DaysTemp();
         DaysTemp t2 = new DaysTemp();

         // Write to the fields of each instance
         t1.High = 76;     t1.Low = 57;
         t2.High = 75;     t2.Low = 53;

         // Read from the fields of each instance and call a method of
         // each instance
         Console.WriteLine("t1: {0}, {1}, {2}",
                                   t1.High, t1.Low, t1.Average() );
         Console.WriteLine("t2: {0}, {1}, {2}",
                                   t2.High, t2.Low, t2.Average() );
                                                         
      }                               Field      Field      Method
   }

This code produces the following output:


t1: 76, 57, 66
t2: 75, 53, 64

Image

Figure 4-8. Memory layout of instances t1 and t2

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.191.181.252