Data Members

   


The following sections look closer at the data members. Previous chapters have already covered instance variables, so the bulk of this part is about static variables and their use.

Instance Variables

When an object is created, its instance variables are filled with values specific to that object, and their values stay specific to this object throughout its lifetime. This mechanism is familiar territory, but worth recapturing to contrast it with static variables discussed in the next section.

Recall our BankSimulation.cs program in Chapter 10, "Arrays Part I: Array Essentials." It consisted of a Bank object referencing a number of Account objects through an array called accounts. The Account class defined, among others, an instance variable called balance as shown in the following:

 class Account {       ...       private decimal balance;       ... } 

The user could specify the Bank object to contain any number of Account objects (in our sample, seven accounts were created). Subsequently, it was possible to alter (by depositing and withdrawing) the instance variable balance of any particular Account object by specifying the account number of the account with which it was associated. In our example, only the balance of the second account was altered to be equal to $110,000.00. The balance of each of the other Account objects remained zero. However, by continuing our simulation, we could also have ended up with seven accounts containing the following balances:

 Account 1: balance equal to 10000 Account 2: balance equal to 110000 Account 3: balance equal to 35000 Account 4: balance equal to 55000 Account 5: balance equal to 300 Account 6: balance equal to 2000 Account 7: balance equal to 3500 

This state of affairs has been depicted in Figure 12.2. The Bank object references seven different Account objects, each with its own unique balance value.

Figure 12.2. Different balance values for different Account objects.
graphics/12fig02.gif

The next section makes the important contrast between the instance variables (illustrated in Figure 12.2) and static variables.

static Variables

Instance variables are used to hold data relevant for an individual object, but what if we have a piece of information that is associated with the whole group of objects belonging to the same class? Let's illustrate what this means by returning to the BankSimulation.cs program in Chapter 10. Recall that we had to decide on the number of accounts initially and that this number would stay fixed for the duration of the program. Suppose we relaxed this condition and could open (create) and close accounts at any time during the execution of this program and wanted to keep track of the total number of Account objects created. Such a data element would not, like the instance variable balance, describe any Account object in particular, but would be associated with the whole group of Account objects. We could attempt to represent the data element by declaring an instance variable called totalAccountsCreated in the Account class:

 class Account {     ...     public uint totalAccountsCreated;   //inefficient     ... } 

It would then be possible to increment totalAccountsCreated by one every time a new Account object is created. However, this approach contains two inefficiencies:

  • Duplication of data totalAccountsCreated describes one attribute about the Account objects as a group. Consequently, only one totalAccountsCreated variable is needed. However, the declaration of totalAccountsCreated shown before causes every single Account object to hold a totalAccountsCreated value identical to all other Account objects. Figure 12.3 shows the duplicated values seven instance variables all containing the value seven after seven Account objects have been created. This is not only a waste of computer memory, but also is confusing for programmers to deal with. Which of the Account objects should we consult to find the total accounts created? The first one? What if no objects were created yet? Then there wouldn't be any objects to provide us with an answer.

    Figure 12.3. totalAccountsCreated contains duplicated values in all Account objects.
    graphics/12fig03.gif
  • Inefficient code due to required update of all Account objects The Bank object would have to update (increment totalAccountsCreated by one) all Account objects every time a new Account object is created. This is acceptable for a small number of accounts, but imagine a bank with hundreds of thousands of bank accounts. Every time a new Account is added, an iteration statement will have to traverse the entire collection of Account objects to perform the many updates.

How can we solve these inefficiency issues? We need to store totalAccountsCreated in just one place, which is removed from each object but still related to the Account objects as a group. Because there is only one Account class and it defines attributes common to all Account objects, the Account class is the natural place to put totalAccountsCreated. The static keyword lets us do exactly that. By declaring totalAccountsCreated to be static as in the following

 class Account {     ...     public static uint totalAccountsCreated;  //Efficient     ... } 

we are specifying that

  • Only one copy of totalAccountsCreated exists, which belongs to the whole Account class rather than its individual objects.

  • totalAccountsCreated is shared between all Account objects.

  • totalAccountsCreated has a value, even if no Account objects have been created yet.

  • totalAccountsCreated can be accessed through the Account class or from within any Account object.

The impact of the static keyword is illustrated in Figure 12.4.

Figure 12.4. A static member variable is shared by all objects of the same class.
graphics/12fig04.gif

Even though totalAccountsCreated is not held by any particular Account object, it can still be accessed and modified from within each object as if it is a standard instance variable. This is demonstrated in the following code snippet where there is no difference between accessing the instance variable accountCreationNumber, and the static variable totalAccountsCreated.

 class Account {     ...     private uint accountCreationNumber;     public static uint totalAccountsCreated;     ...     public void accountsCreatedAfterMe()     {         Console.WriteLine("Number of accounts created after me: { 0} ",             totalAccountsCreated - accountCreationNumber);  //Identical syntax     }     ... } 

Accessing totalAccountsCreated from outside an Account object must be done by applying the dot operator to the classname Account as in the following:

 class Bank {     ...     ...Account.totalAccountsCreated...     ... } 

This way of accessing totalAccountsCreated is not valid if totalAccountsCreated is private, but is valid if it is declared public.

Note

graphics/common.gif

Two of the access modifiers available to declare a static member variable are public and private. As a rule of thumb, all static member variables should, like their instance variable cousins, be declared private to abide with the encapsulation principles. For demonstration purposes, I have broken this rule and declared totalAccountsCreated to be public in this section. If a static member variable is declared private, it is only possible to access and modify it through the standard kind of methods we have met so far, or through static methods that, like static member variables, are associated with a class instead of its objects. static methods are discussed later in this chapter.


Note

graphics/common.gif

A static member variable (like totalAccountsCreated) cannot be accessed by applying the dot operator to the name of an individual object (like Account) as in the following:

 class Bank {     ...     Account myAccount = new Account();     ...myAccount.totalAccountsCreated...     //invalid     ... } 


Listing 12.1 demonstrates how the public static member variable totalAccountsCreated (line 6) can be accessed, modified, and thereby utilized. The program called DynamicBankSimulation.cs contains stripped down versions of the three classes Account, Bank, and BankSimulation found in the BankSimulation.cs program of Chapter 10. Whereas the BankSimulation.cs program obliged us to specify a fixed number of accounts at the beginning of the program, DynamicBankSimulation.cs allows us to add and remove bank accounts during the runtime of the program. The program also keeps track of the total number of bank accounts created (held in totalAccountsCreated) and the current amount of accounts held by the bigBucksBank.

Listing 12.1 DynamicBankSimulation.cs
01: using System; 02: using System.Collections; 03: 04: class Account 05: { 06:     public static uint totalAccountsCreated = 0; 07: 08:     public Account() 09:     { 10:         totalAccountsCreated++; 11:     } 12: } 13: 14: class Bank 15: { 16:     private ArrayList accounts; 17: 18:     public Bank() 19:     { 20:         Console.WriteLine("Congratulations! You have created a new bank"); 21:         accounts = new ArrayList(); 22:     } 23: 24:     public void AddNewAccount() 25:     { 26:         accounts.Add(new Account()); 27:         Console.WriteLine("New account added!"); 28:         PrintStatistics(); 29:     } 30: 31:     public void RemoveFirstAccount() 32:     { 33:         if (accounts.Count > 0) 34:         { 35:             accounts.RemoveAt(0); 36:             Console.WriteLine("Account removed!\n"); 37:             PrintStatistics(); 38:         } 39:         else 40:         { 41:             Console.WriteLine("Sorry no more current accounts"); 42:         } 43:     } 44: 45:     public void PrintStatistics() 46:     { 47:         Console.WriteLine("Number of current accounts:" + accounts.Count + 48:             "\nNumber of new accounts created: " + Account.totalAccountsCreated); 49:     } 50: } 51:  52: class DynamicBankSimulation 53: { 54:     private static Bank bigBucksBank; 55: 56:     public static void Main() 57:     { 58:         string command; 59: 60:         bigBucksBank = new Bank(); 61:         do 62:         { 63:             PrintMenu(); 64:             command = Console.ReadLine().ToUpper(); 65:             switch (command) 66:             { 67:                 case "A": 68:                     bigBucksBank.AddNewAccount(); 69:                     break; 70:                 case "E": 71:                     Console.WriteLine("Bye Bye!"); 72:                     break; 73:                 case "R": 74:                     bigBucksBank.RemoveFirstAccount(); 75:                     break; 76:                 case "P": 77:                     bigBucksBank.PrintStatistics(); 78:                     break; 79:                 default: 80:                     Console.WriteLine("Invalid choice"); 81:                     break; 82:             } 83:         }  while (command != "E"); 84:     } 85: 86:     private static void PrintMenu() 87:     { 88:         Console.WriteLine("\nWhat would you like to do?\n" + 89:             "A) Add new account\n" + 90:             "R) Remove account\n" + 91:             "P) Print statistics\n" + 92:             "E) End session\n"); 93:     } 94: } Congratulations! You have created a new bank What would you like to do? A) Add new account R) Remove account P) Print statistics E) End session P<enter> Number of current accounts: 0 Number of new accounts created: 0 What would you like to do? ... A<enter> New account added! Number of current accounts: 1 Number of new accounts created: 1 What would you like to do? ... A<enter> New account added! Number of current accounts: 2 Number of new accounts created: 2 What would you like to do? ... R<enter> Account removed! Number of current accounts: 1 Number of new accounts created: 2 What would you like to do? ... E<enter> Bye Bye! 

Note:

  • For space reasons, the menu has only been shown once and then replaced by three dots (…).

For space reasons, the Account class has no balance instance variable or the likes. Line 6 declares totalAccountsCreated to be a static member variable of type uint. It is initialized to zero as soon as the program is started, which allows us to access it even before any Account objects are created. This is demonstrated with the P<enter> command in the first part of the sample output, letting the user know that zero accounts have been created and zero accounts currently exist. The P command triggers, via the switch section in lines 76 78, the PrintStatistics method (lines 45 49) of the Bank object called bigBucksBank to be executed.

totalAccountsCreated is accessed in line 48 by applying the dot operator to the classname Account as in Account.totalAccountsCreated.

totalAccountsCreated can be accessed and modified from within an Account object as if it is a standard instance variable; this is demonstrated by the constructor (lines 8 11) of Account. Recall that the constructor is called every time a new object is created, in this case, causing totalAccountsCreated to be incremented by one (line 10) every time a new Account object is created.

The standard C# array introduced in Chapter 10 has a fixed, unchangeable length after it is created. In our demonstration, we need an array-like construct that can hold a collection of objects but with an ability to grow and shrink during its lifetime. The ArrayList class found in the namespace called System.Collections of the .NET Framework contains this functionality. The fully-qualified name for ArrayList is System.Collections.ArrayList but, due to line 2 (using System.Collections;), we can use the shortcut ArrayList, as in lines 16 and 21. Like the string and Array classes, ArrayList contains a useful assortment of built-in methods. However, our program merely uses its two methods Add (line 26) and RemoveAt (line 35), along with the property Count (lines 33 and 47). When the accounts instance (declared in line 16) is created in the constructor of a Bank object (line 21), its length is 0.

An ArrayList object will hold any object, so we do not, as with the Array, need to specify its base type. We can add a new Account object to accounts with the Add method by providing it an Account object reference as an argument (see line 26). When an object is added to accounts with the Add method, it is added to the end of the already existing list of objects, just as when people arrive at a queue. RemoveAt lets us specify the index (passed as an argument) of the particular object we want to remove (as in line 35), which happens to remove the first object in the list.

The Count property provides us with the current number of objects held by the ArrayList.

There is a difference between the number Count represents and the number totalAccountsCreated represents totalAccountsCreated counts the number of times the event: "Create a new account" has taken place. On the other hand, Count keeps track of the actual number of objects stored inside accounts. Thus, whereas Count moves up when account objects are added and down when they are removed, totalAccountsCreated only moves up when an account is added, but is not affected by any Account object removals.

Constant Members

I have already discussed constant members in Chapter 6, "Types Part I: The Simple Types." However, now that you are familiar with static variables, this is an appropriate place to mention that constant members are static variables by default. Let's illustrate the implications with an example. Imagine that you are constructing a simple computer model of the solar system. You are particularly interested in how light travels between the sun and the other planets. To this end, you create a class called Light. Specific objects of Light can then be used to represent particular sunrays. You need the constant speed of light (approximately 299792 kilometers per second) for many of your calculations, and so you include it in your Light class as in the following lines:

 class Light {     ...     public const double Speed = 299792;     ... } 

Even though there is no static keyword in sight, Speed is now not only constant but also static, and it can be accessed from another object simply by applying the dot operator to the class name Light:

 ...Light.Speed... 

readonly Members

Because readonly members are closely connected to the constructor concept, their discussion is deferred until after the section about constructors in Chapter 13, "Class Anatomy Part II: Object Creation and Garbage Collection."

Declaring Data Members: An Overview

This section provides a brief overview of the syntax for declaring each of the mentioned data members.

Syntax Box 12.2 only shows the most important parts of a data member declaration. The declaration in this syntax box is spread over four lines because one line was too wide for the page, so the definition should be read as if the four lines were one line.

If, for a moment, we disregard the second line

 [static] [const  |  readonly] 

(this is possible because it only consists of optional elements), we can, with the remaining parts, form the following familiar instance variable declaration:

 private decimal amount = 0; 

and by including the optional static keyword as in

 private static int counter = 0; 

we declare counter to be a static member variable.

A constant is, as we have seen earlier, declared by including the const keyword, as in the following line:

 const uint SpeedOfLight = 299792; 

The static keyword cannot be applied in a constant declaration, because a constant is static by default. For reasons of simplicity, this has not been expressed in Syntax Box 12.2.

A constant must have its value defined by compile time and must have a value assigned to it as part of its declaration.

The readonly member variable is declared with the readonly keyword, as in the next line of code. As opposed to a constant, a readonly member variable is not static by default and can be declared static as shown here.

 private static readonly int counter = 0; 

Syntax Box 12.2 Declaring a Data Member

 Data_member_declaration::= [<Access_modifier>] [static] [const  |  readonly] <Type> <Member_variable1_identifier> [= <Expression1>] [, <Member_variable2_Identifier> [= <Expression2> ] ...]; 

where

 <Access_modifier>              ::=  public              ::=  private              ::=  protected              ::=  internal              ::=  protected internal 

Notes:

  • A simplified version of the syntax for a member variable declaration was shown in Syntax Box 6.1 in Chapter 6. This syntax box expands this initial definition.

  • So far, we have only discussed the public and private access modifiers. protected, internal, and protected internal have merely been included here for completeness. These latter modifiers will be discussed in Chapter 15, "Namespaces, Compilation Units, and Assemblies," and Chapter 16, "Inheritance Part I: Basic Concepts," and can safely be ignored at this point.


   


C# Primer Plus
C Primer Plus (5th Edition)
ISBN: 0672326965
EAN: 2147483647
Year: 2000
Pages: 286
Authors: Stephen Prata

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net