Implementing Indexed Properties


Now that you know how to implement a scalar property, let’s move on to consider indexed properties, also known as indexers. These are useful for classes that have data members that are collections of items, and where you might want to access one of the items in the collection.

The Bank Example

An example might be a Bank class that maintains a collection of Accounts. If you’re not using properties, you’d tend to see code such as the following being used to access members of the Bank class:

// Get a pointer to one of the Accounts held by the Bank Account* pacc = theBank->getAccount(1234567);

An indexed property will let you access the Account members using array notation, like this:

// Get a pointer to one of the accounts held by the Bank Account* pacc = theBank->Account[1234567];

You can implement get and set methods for indexed properties so that you can use them on both sides of the equal sign (=). The following code fragment uses two properties, with the first indexed property giving access to an account and the second giving access to an overdraft limit:

// Set the overdraft limit for one of the accounts theBank->Account[1234567]->OverDraft = 250.0;

The following longer exercise walks you through implementing the Bank and Account classes, and it also shows you how to create and use the scalar and indexed properties.

Implementing the Bank Class

  1. Start Visual Studio .NET, and open a new Visual C++ Console Application (.NET) project named Banker.

  2. Add the definition for a new class named Bank by right-clicking the project name in Solution Explorer, choosing Add from the pop-up menu, and then choosing Add Class from the hierarchical menu that appears.

  3. The Add Class dialog box opens, in which you choose the type of class you’re going to add. Make sure that the Generic C++ Class icon is selected before you click Open.

    click to expand

  4. The Generic C++ Class Wizard constructs the skeleton of a new class for you. Fill in the fields as shown in the following figure, and then click Finish.

    click to expand

    The wizard creates a generic class that simply consists of a constructor and a destructor. The class definition will be in the Bank.h file, and the class implementation will be in the Bank.cpp file.

  5. You need to edit the class code to fit this application. Open the header file Bank.h, and perform the following three edits:

    • Delete the destructor definition from the class.

    • Add the __gc keyword to the class definition because you need a managed class.

  • Type a #using <mscorlib.dll> line immediately after the #pragma once declaration. This line is needed when defining managed types.

  • You should end up with a header file that looks like this:

    #pragma once #using <mscorlib.dll> __gc class Bank { public: Bank(void); };
  1. Open the implementation file Bank.cpp, and make the corresponding changes:

    • Remove the destructor implementation.

    • Add a call to Console::WriteLine in the constructor so that you can verify that it has been called.

  • Add using namespace System; after the #using line so that Console and other classes in the System namespace can be used without further qualification.

    The file should look like this when you’ve finished editing:

    #include "StdAfx.h" #include ".\bank.h" #using <mscorlib.dll> using namespace System; Bank::Bank(void) { Console::WriteLine(S"Bank: constructor"); }
  1. To ensure that everything is correct, open the Banker.cpp file and add code to the _tmain function to create a Bank object:

    int _tmain() { Console::WriteLine(S"Bank Example"); // Create a Bank object Bank* theBank = new Bank(); return 0; } 
  2. You must also include Bank.h from the Banker.cpp file so that the compiler will know where to locate the declaration of the Bank class. Add the following code to Banker.cpp after the #include “stdafx.h” line:

    #include "Bank.h"

    Compile and run this code, and you should see the constructor message being printed on the console.

Adding the Account Class

The next stage involves creating the Account class, using the Generic C++ Class Wizard in very much the same way.

  1. Add a class named Account to the project used in the previous exercise, in exactly the same way as you added the Bank class.

  2. Make the same edits to the Account class as you did to the Bank class. You should end up with a header file named Account.h that looks like this:

    #pragma once #using <mscorlib.dll> __gc class Account { public: Account(void); };

    and an implementation file Account.cpp that looks like this:

    #include "StdAfx.h" #include ".\Account.h" #using <mscorlib.dll> using namespace System; Account::Account() { Console::WriteLine(S"Account: constructor"); }
  3. Add some structure to the Account class. Accounts will have an account number, a balance, and an overdraft limit, so add three private members to the Account class definition in Account.h, like this:

    long accNumber; // the account number double balance; // the current balance double limit; // the overdraft limit
  4. Edit the constructor definition and implementation as follows so that three values are passed in and used to initialize these three variables:

    Account::Account(long num, double bal, double lim) { Console::WriteLine(S"Account: constructor"); // Basic sanity check if (num < 0 || lim < 0) throw new ArgumentException(S"Bad arguments to constructor"); // Initialize values accNumber = num; balance = bal; limit = lim; }

    The basic sanity check simply checks that the account number and overdraft limit aren’t negative, and it throws an ArgumentException if they are.

Creating Account Class Properties

Once the Account class has been constructed, you can add properties to allow access to the three data members. All three members are scalar so the properties are easy to implement.

  1. Add a public get method to Account.h to allow read-only access to the account number, as shown here:

    __property long get_AccountNumber() { return accNumber; }

    You can add the function definition inline in the class definition. Remember to put it in the public section!

  2. You also need to add a read-only property for the balance member because in real life, you don’t want people simply modifying the balances in their accounts from code.

    __property double get_Balance() { return balance; }
  3. Add a read/write property for the overdraft limit because it’s quite possible that the limit might get changed from time to time.

    __property double get_Limit() { return limit; } __property void set_Limit(double lim) { if (lim < 0) throw new ArgumentException(S"Limit can’t be negative"); limit = lim; }

    If you choose to implement these properties inline in the class definition, you’ll need to add a using namespace System; line or fully qualify the name of ArgumentException before the code will compile.

  4. Test out your implementation by adding some code to the _tmain function in Banker.cpp to create a new Account object and access its properties. Include the Account.h file, and then add code to create an Account object, like this:

     // Create an Account object Account* theAccount = new Account(123456, 0.0, 0.0);

Adding Accounts to the Bank Class

The purpose of the Bank class is to hold Accounts, so the next step is to modify the Bank class to hold a collection of Account objects. Rather than design something from scratch, you’ll use the System::Collections::ArrayList class (which you met in Chapter 12) to hold the Accounts.

Implementing the Add and Remove Methods

The Add and Remove methods provide a way to manipulate the collection of Accounts held by the Bank class.

  1. Open the Bank.h header file. Add the following two lines of code immediately after the #using <mscorlib.dll> line at the top of the file:

    using namespace System::Collections; #include "Account.h"

    The using declaration will make it easier to use ArrayList in the Bank class, and you’ll need to reference the Account class later.

  2. Add an ArrayList variable to the Bank class, making sure that it’s private.

    ArrayList* accts;
  3. Add the following line of code to the Bank constructor to create the ArrayList member:

    accts = new ArrayList();
  4. Add the code for the public Add method inline in the header file, as follows:

    bool Add(Account* pAcc) { // check if the account is already in the list if (accts->Contains(pAcc)) return false; else accts->Add(pAcc); return true; }

    Add takes a pointer to an Account object and then uses the ArrayList Contains method to check whether the account already exists in the collection. If it doesn’t, Add adds the Account to the collection. Remove is very similar.

    bool Remove(Account* pAcc) { // check if the account is already in the list if (accts->Contains(pAcc)) { accts->Remove(pAcc); return true; } else return false; }

    Remove checks whether an Account is in the ArrayList and removes it if the account is there. It isn’t necessary to call Contains because Remove will silently do nothing if you try to remove an item that isn’t in the list. Users, though, might be interested in knowing that the account they’re trying to remove isn’t in the collection already.

Implementing an Indexed Property to Retrieve Accounts

You can now manipulate the collection of Accounts, adding and removing items. If you want to look up a particular account, you’ll probably want to do so by the account number, and an indexed property provides a good way to access accounts by account number. You’ll only need to retrieve Account references using the property, so you’ll implement a read-only indexed property.

  1. Open the Bank.h header file if it isn’t already open.

  2. Add the following code to implement the property:

    // Indexed property to return an account __property Account* get_Acct(long number) { IEnumerator* ie = accts->GetEnumerator(); while (ie->MoveNext()) { Account* pa = dynamic_cast<Account*>(ie->get_Current()); if (pa->AccountNumber == number) return pa; } throw new ArgumentOutOfRangeException(S"Bad account number"); }

    You might wonder why the property isn’t named get_Account. The problem is that this name would result in a virtual data member named Account being added to the Bank class, which would clash with the name of the Account class.

    The function uses an enumerator—discussed in Chapter 12—to iterate over the Accounts in the ArrayList. As long as there are more elements in the collection, MoveNext moves to the next element, which can then be accessed using the Current method. The pointer returned by Current is a generic Object*, so it needs to be cast into an Account* before you can get at the account number. The cast is done using a dynamic cast, but you don’t need to check the return from the cast in this case because you can be certain that there is nothing except Account pointers in the collection.

    When you find an account whose number matches the one passed in, the pointer is returned. If no such account is found, an exception is thrown because trying to access a nonexistent account is equivalent to reading off the end of an array: it’s a serious error that should be signaled to the caller.

  3. Test out the Bank class by adding some code to the _tmain function in Banker.cpp. You’ll need to start by making sure that the Bank.h and Account.h header files are included. Next add some code so that your _tmain function is similar to the following:

    int _tmain() { Console::WriteLine(S"Bank example"); // Create a bank Bank* theBank = new Bank(); // Create some accounts Account* accountOne = new Account(123456, 100.0, 0.0); Account* accountTwo = new Account(234567, 1000.0, 100.0); Account* accountThree = new Account(345678, 10000.0, 1000.0); // Add them to the Bank theBank->Add(accountOne); theBank->Add(accountTwo); theBank->Add(accountThree); // Use the indexed property to access an account Account* pa = theBank->Acct[234567]; Console::WriteLine(S"Balance is {0}", __box(pa->get_Balance())); return 0; }

    After creating a Bank and a number of Account objects, you add the Account objects to the Bank collection by calling Add. You can then use the indexed property to access an account by number and use that pointer to display the balance. Test the property by passing in an account number that doesn’t exist, and check that an exception is thrown.




Microsoft Visual C++  .NET(c) Step by Step
Microsoft Visual C++ .NET(c) Step by Step
ISBN: 735615675
EAN: N/A
Year: 2003
Pages: 208

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