Home

Collections: Implementing IList

 

The Size of a Collection

 

A Fixed-Size Collection

In the next sections, we will see how to add values to a list of a database. As you add or insert values in a list, the Count property grows. If your collection is array-based, when you start it, you specify the number of values that the list will contain.

In theory, you cannot add new values beyond that number. In reality, you can increase the size of an array and then add a new item. If your collection is a linked list, you are also not confined to the laws of space (unless your computer runs out of memory).

If you create a list whose number of values must be constant, the user cannot add values beyond the maximum allowed number. Therefore, before adding a value, you can first check whether the collection has a fixed size or not. To give you this information, the IList interface is equipped with a Boolean read-only property named IsFxedSize. This property simply lets the user know whether the collection has a fixed number of items.

Here is an example of implementing this property for the System::Collections::IList interface:

#pragma once
using namespace System;
using namespace System::Collections;

public ref class CBookList : public IList
{
private:
    int counter;
    array<Object ^> ^ objs;

public:
    CBookList(void);

    virtual property int Count
    {
        int get() { return counter; }
    }

    virtual property bool IsSynchronized
    {
        bool get() { return false; }
    }

    virtual property Object ^ SyncRoot
    {
        Object ^ get() { return this; }
    }

    virtual void CopyTo(Array ^ ary, int index);
    virtual IEnumerator ^ GetEnumerator();

    virtual property bool IsFixedSize
    {
        bool get() { return false; }
    }
};

Practical LearningPractical Learning: Implementing the IsFixedSize Property

  1. Change the StoreItems.h header file as follows:
     
    #pragma once
    using namespace System;
    using namespace System::Collections;
    
    [Serializable]
    ref class CStoreItems : public IList
    {
    
    public:
        CStoreItems(void);
        
            . . . No Change
    
        virtual property bool IsFixedSize
        {
            bool get() { return false; }
        }
    
        virtual void CopyTo(Array ^ arr, int index);
        virtual IEnumerator ^ GetEnumerator();
    };
  2. Save the file

A Read-Only Collection

Most databases are meant to receive new values. If you want, you can create a list that cannot receive new values. To support this, the IList interface is equipped with the Boolean IsReadOnly property. If a list is read-only, it would prevent the clients from adding items to it.

Here is an example of implementing the IsReadOnly property for the System::Collections::IList interface:

#pragma once
using namespace System;
using namespace System::Collections;

public ref class CBookList : public IList
{
private:
    int counter;
    array<Object ^> ^ objs;

public:
    CBookList(void);

    virtual property int Count
    {
        int get() { return counter; }
    }

    virtual property bool IsSynchronized
    {
        bool get() { return false; }
    }

    virtual property Object ^ SyncRoot
    {
        Object ^ get() { return this; }
    }

    virtual void CopyTo(Array ^ ary, int index);
    virtual IEnumerator ^ GetEnumerator();

    virtual property bool IsFixedSize
    {
        bool get() { return false; }
    }

    virtual property bool IsReadOnly
    {
        bool get() { return false; }
    }
};

Practical Learning Practical Learning: Setting the Read-Only Effect

  1. Change the StoreItems.h header file as follows:
     
    #pragma once
    using namespace System;
    using namespace System::Collections;
    
    [Serializable]
    ref class CStoreItems : public IList
    {
    
    public:
        CStoreItems(void);
    
        . . . No Change
    
        virtual property bool IsReadOnly
        {
            bool get() { return false; }
        }
    
        virtual void CopyTo(Array ^ arr, int index);
        virtual IEnumerator ^ GetEnumerator();
    };
  2. If you try executing the application now, you would receive various errors indicating that the CStoreItems class has not implemented all required methods. Therefore, to eliminate these errors and prepare the class for later sections, change the header file as follows:
     
    #pragma once
    using namespace System;
    using namespace System::Collections;
    
    [Serializable]
    ref class CStoreItems : public IList
    {
    private:
        int counter;
        array<Object ^> ^ items;
    
    public:
        CStoreItems(void);
    
        virtual property int Count
        {
            int get() { return counter; }
        }
    
        virtual property bool IsSynchronized
        {
            bool get() { return false; }
        }
    
        virtual property Object ^ SyncRoot
        {
            Object ^ get() { return this; }
        }
    
        virtual property bool IsFixedSize
        {
            bool get() { return false; }
        }
    
        virtual property bool IsReadOnly
        {
            bool get() { return false; }
        }
    	
        virtual property Object ^ default[int]
        {
            Object ^ get(int index) { return nullptr; }
            void set(int index, Object ^ value) { }
        }
    
        virtual void CopyTo(Array ^ arr, int index);
        virtual IEnumerator ^ GetEnumerator();
        virtual int Add(Object ^ value);
        virtual void Insert(int index, Object ^ value);
        virtual bool Contains(Object ^ value);
        virtual int IndexOf(Object ^ value);
        virtual void RemoveAt(int index);
        virtual void Remove(Object ^ value);
        virtual void Clear();
    };
  3. Change the StoreItems.h source file as follows:
     
    #include "StdAfx.h"
    #include "StoreItems.h"
    
    CStoreItems::CStoreItems(void)
        : counter(0),
          items(gcnew array<Object ^>(5))
    {
    }
    
    void CStoreItems::CopyTo(Array ^ arr, int index)
    {
    }
    
    IEnumerator ^ CStoreItems::GetEnumerator()
    {
        return nullptr;
    }
    
    // This method is used to add a new item to the collection
    int CStoreItems::Add(Object ^ value)
    {
        return 0;
    }
    
    // This method can be used to insert an item at 
    // a certain position inside the collection
    void CStoreItems::Insert(int index, Object ^ value)
    {
    }
    
    // This method is used to find out whether the item 
    // passed as argument exists in the collection
    bool CStoreItems::Contains(Object ^ value)
    {
        return false;
    }
    
    // This method is used to check whether the item passed as
    // argument exists in the collection. If so, it returns its index
    int CStoreItems::IndexOf(Object ^ value)
    {
        return 0;
    }
    
    // This method is used to delete the item positioned 
    // at the index passed as argument
    void CStoreItems::RemoveAt(int index)
    {
    }
    
    // This method first checks the existence of the item passed 
    //  as argument. If the item exists, the method deletes it
    void CStoreItems::Remove(Object ^ value)
    {
    }
    
    // This methods deletes all items from the collection
    void CStoreItems::Clear()
    {
    }
  4. Save all
 

 

 

Populating the Collection

 

Adding an Item

As it should be obvious, the primary operation to perform on a list is to populate it with at least one value. To support this, the System::Collections::IList interface is equipped with a method named Add. Its syntax is:

int Add(object value);

This method takes one argument as the value to add to the list. If your collection is an array, you can first check that there is still enough room in the list to add a new item. In reality, this is never an issue with the System::Collections::IList interface:

  • If there is still room in the collection, the value would be added to the list
  • If there is not enough room, the value would simply not be added. There would not be a problem and the program would not crash. In fact, no exception would be thrown if the value was not added because of lack of space. On the other hand, since the compiler would not let you know that there was a problem with "logistic", you may not know whether the value was added or not. Therefore, if you are concerned with knowing whether the value was added, you must provide this functionality yourself 

If the method succeeds with the addition, it returns the position where the value was added in the list. This is usually the last position in the list.

Here is an example:

Header File: BookList.h

#pragma once
using namespace System;
using namespace System::Collections;

public ref class CBookList : public IList
{
private:
    int counter;
    array<Object ^> ^ objects;

public:
    CBookList(void);

    virtual property int Count
    {
        int get() { return counter; }
    }

    virtual property bool IsSynchronized
    {
        bool get() { return false; }
    }

    virtual property Object ^ SyncRoot
    {
        Object ^ get() { return this; }
    }

    virtual void CopyTo(Array ^ ary, int index);
    virtual IEnumerator ^ GetEnumerator();

    virtual property bool IsFixedSize
    {
        bool get() { return false; }
    }

    virtual property bool IsReadOnly
    {
        bool get() { return false; }
    }

    virtual int Add(Object ^ value);
};

Source File: BookList.cpp

#include "BookList.h"

CBookList::CBookList(void)
	: counter(0),
      objects(gcnew array<Object ^>(5))
{
}

void CBookList::CopyTo(Array ^ ary, int index)
{
}

IEnumerator ^ CBookList::GetEnumerator()
{
    return nullptr;
}

int CBookList::Add(Object ^ value)
{
    // Check whether there is still room in
    // the array to add a new item
    if (counter < objects->Length)
    {
        // Since there is room, put the new item to the end
        objects[counter] = value;
        // increase the number of items
        counter++;
        // Return the index of the item that was added
        return counter - 1;
    } // Since the item could not be added, return a negative index
    else
        return -1;
}

Practical LearningPractical Learning: Adding an Item to the Collection

  1. Change the code of the CStoreItems::Add() method in the StoreItems.cpp source file as follows:
     
    // This method is used to add a new item to the collection
    int CStoreItems::Add(Object ^ value)
    {
        // Find out if the array is getting too small for the next item(s)
        // If it is, increase its size by 5
        if (Count == items->Length)
            Array::Resize(items, items->Length + 5);
    
        if (counter < items->Length)
        {
            items[counter] = value;
            counter++;
            return counter - 1;
        }
        else
            return -1;
    }
  2. In the Class View, expand the MusicalInstrumentStore2 sub-folder. Under MusicalInstrumentStore2, double-click ItemEditor
  3. In the top section of the file, add the following:
     
    #pragma once
    
    #include "CategoryEditor.h"
    #include "TypeEditor.h"
    #include "StoreItem.h"
    #include "StoreItems.h"
    
    using namespace System;
    using namespace System::ComponentModel;
    using namespace System::Collections;
    using namespace System::Windows::Forms;
    using namespace System::Data;
    using namespace System::Drawing;
    using namespace System::IO;
    using namespace System::Runtime::Serialization::Formatters::Binary;
  4. In the Solution Explorer, double-click ItemEditor.h
  5. On the form, double-click the Create button and implement its event as follows:
     
    System::Void btnCreate_Click(System::Object^  sender, System::EventArgs^  e)
    {
        FileStream ^ stmStoreItem = nullptr;
        CStoreItem ^ item = gcnew CStoreItem;
        CStoreItems ^ items = gcnew CStoreItems;
        BinaryFormatter ^ bfmStoreItem = gcnew BinaryFormatter;
    
        // If this directory doesn't exist, create it
        Directory::CreateDirectory(L"C:\\Musical Instrument Store");
        // This is the file that holds the list of items
        String ^ Filename = "C:\\Musical Instrument Store\\StoreItems.mis";
    
        // Create a random number that will be used to identify the item
        Random ^ rnd = gcnew Random;
        txtItemNumber->Text = rnd->Next(100000, 999999).ToString();
    
        // Make sure the user had selected a category
        if (cbxCategories->Text->Length == 0)
        {
            MessageBox::Show(L"You must specify the item's category");
            cbxCategories->Focus();
            return;
        }
    
        // Make sure the user had selected a type
        if (cbxTypes->Text->Length == 0)
        {
            MessageBox::Show(L"You must specify the item's type");
            cbxTypes->Focus();
            return;
        }
    
        // Make sure the user had entered a name/description
        if (txtItemName->Text->Length == 0)
        {
             MessageBox::Show(L"You must enter the name (or a " 
                              L"short description) for the item");
             txtItemName->Focus();
             return;
        }
    
        // Make sure the user had typed a price for the item
        if (txtUnitPrice->Text->Length == 0)
        {
             MessageBox::Show(L"You must enter the price of the item");
             txtUnitPrice->Focus();
             return;
        }
    
        // Before saving the new item, find out if there was
        // already a file that holds the list of items
        // If that file exists, open it and store its items 
        // in our StoreItems list
        if( File::Exists(Filename))
        {
             stmStoreItem = gcnew FileStream(Filename,
                                           FileMode::Open,
                                           FileAccess::Read,
                                           FileShare::Read);
    
             try
             {
                  // Retrieve the list of items from file
                  items = 
    	dynamic_cast<CStoreItems ^>(bfmStoreItem->Deserialize(stmStoreItem));
              }
              finally
              {
                  stmStoreItem->Close();
              }
          }
    
          // Create the music item
          item->ItemNumber = txtItemNumber->Text;
          item->Category = cbxCategories->Text;
          item->Type = cbxTypes->Text;
          item->ItemName = txtItemName->Text;
          item->UnitPrice = double::Parse(txtUnitPrice->Text);
    
          // Call the Add method of our collection class to add the item
          items->Add(item);
    
          // Save the list
          stmStoreItem = gcnew FileStream(Filename,
                                        FileMode::Create,
                                        FileAccess::Write,
                                        FileShare::Write);
    
          try
          {
              bfmStoreItem->Serialize(stmStoreItem, items);
    
              if (txtPicturePath->Text->Length != 0)
              {
                    FileInfo ^ flePicture = gcnew FileInfo(txtPicturePath->Text);
                    flePicture->CopyTo(L"C:\\Musical Instrument Store\\" +
                                      txtItemNumber->Text + flePicture->Extension);
              }
    
              // After saving the item, reset the form
              txtItemNumber->Text = rnd->Next(100000, 999999).ToString();
              cbxCategories->Text = L"";
              cbxTypes->Text = L"";
              txtItemName->Text = L"";
              txtUnitPrice->Text = L"0.00";
              txtPicturePath->Text = L"";
              pbxStoreItem->Image = nullptr;
        }
        finally
        {
            stmStoreItem->Close();
        }
    }
  6. Save the file

Inserting an Item

When you call the System::Collections::IList::Add() method, it adds the new value to the end of the list. Sometimes, you will want the new value to be insert somewhere inside the list. To support this operation, both the System::Collections::IList and the System::Collections::Generic::IList interfaces provide a method named Insert. The syntax of the System::Collections::IList::Insert() method is:

void Insert(int index, Object^ value);

The syntax of the System::Collections::Generic::IList::Insert() method is:

void Insert(int index, T item);

This method takes two arguments. The second argument is the value that will be inserted into the list. The argument must hold a valid value. Because this method takes an Object object, if your collection is using a different type of value, you may have to cast it to Object. The first argument is the index of the item that will precede the new one.

As mentioned for the System::Collections::IList::Add() method, there are a few things you should know about this operation's success or lack of it:

  • If the index argument holds a negative value or a value higher than the allowed number (for example if the list is an array) of the items (depending on how you implement the method), the new value would not be added, the compiler would not throw an exception, and therefore nothing would let you know that the value was not added. If you want to find out whether the value was formally or actually inserted or not, you must create the functionality yourself
  • If the value argument is not valid, again depending on how you structure your class, either the value would not be inserted or something else would go wrong. Fortunately, if the value argument is of the type of a class you created yourself, the compiler would produce an error such as letting you know that the argument is holding a value that is not conform to its property or member variable
 
 
   
 

Previous Copyright © 2009-2010 FunctionX, Inc. Next