Home

Introduction to Collections

 

Overview of Collections

 

Introduction

A database is collection of values. To create these values, you can use a collection class. Fortunately, instead of creating a class from scratch, the .NET Framework provides a very impressive library of interfaces and collection classes. The built-in interfaces of the .NET Framework lay a foundation that other classes use to implement and customize the desired functionality.

 

The various built-in collection classes are meant to satisfy various purposes. Some classes are available to any application and can be used by any Windows control. Some other collection classes are nested in classes that particularly need them.

The .NET Framework supports collections in various namespaces. While the System::Collections namespace provides regular collection classes, the System::Collections::Generic namespace contains the equivalent generic classes. 

Accessories for Collections

One the most routines operations performed on a database consists of reviewing its values. To assist you with this, the .NET Framework provides the IEnumerator and the IEnumerable interfaces that are defined in the System::Collections namespace. Their generic equivalences can be found in the System::Collections::Generic namespace. After implementing these interfaces, you can use the for each operator to visit each value of the database.

To implement the System::Collections::IEnumerator interface, you must derive a class from it. Then, you must define the Reset(), the MoveNext() methods, and the Current property. Here is an example:

Header File: Enumeration.h

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

ref class CEnumeration : public IEnumerator
{
private:
    array<String ^> ^ names;
    int cur;
public:
    CEnumeration(void);
	
    CEnumeration(array<String ^> ^ list);
   
    virtual property Object ^ Current
    {
        Object ^ get() { return names[cur]; }
    }

    virtual void Reset();
    virtual bool MoveNext();
};

Source File: Enumeration.cpp

#include "Enumeration.h"

CEnumeration::CEnumeration(void)
{
}

CEnumeration::CEnumeration(array<String ^> ^ list)
{
    this->names = list;
    cur = -1;
}

void CEnumeration::Reset()
{
    cur = -1;
}

bool CEnumeration::MoveNext()
{
    cur++;

    if (cur < names->Length)
        return true;
    else
        return false;
}

To implement the System::Collections::IEnumerable interface, you must derive a class from it. When implementing the class, you must define an accessory method and the GetEnumerator() method that returns an IEnumerator object. Here is an example:

Header File: Enumerable.h

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

ref class CEnumerable : public IEnumerable
{
private:
    array<String ^> ^ names;
public:
    CEnumerable(void);
	
    void Identify(array<String ^> ^ values);
    virtual IEnumerator ^ GetEnumerator();
};

Source File: Enumerable.cpp

#include "Enumerable.h"
#include "Enumeration.h"

CEnumerable::CEnumerable(void)
{
}

void CEnumerable::Identify(array<String ^> ^ values)
{
    names = values;

    for(int i = 0; i < values->Length; i++)
        names[i] = values[i];
}

IEnumerator ^ CEnumerable::GetEnumerator()
{
    return gcnew CEnumeration(names);
}

Once you have implemented the interfaces, you can use for each. Here is an example:

#pragma once

#include <windows.h>
#include "Person.h"
#include "Enumerable.h"

#using <System.dll>
#using <System.Drawing.dll>
#using <System.Windows.Forms.dll>

using namespace System;
using namespace System::Drawing;
using namespace System::Windows::Forms;

public ref class CExercise : public Form
{
private:
	ListBox ^ lbxNames;

	void FormLoader(Object ^ sender, EventArgs ^ e);
	void InitializeComponents();
	
public:
	CExercise();
};

CExercise::CExercise()
{
	InitializeComponents();
}

void CExercise::InitializeComponents()
{
    lbxNames = gcnew ListBox;
    lbxNames->Location = Point(12, 12);
    lbxNames->Width = 140;
    lbxNames->Height = 110;
    Controls->Add(lbxNames);

    Text = L"Employees Names";
    StartPosition = FormStartPosition::CenterScreen;
    FormBorderStyle = System::Windows::Forms::FormBorderStyle::FixedDialog;
    MinimizeBox = false;
    MaximizeBox = false;
    Width = 175;
    Height = 160;
    Load += gcnew EventHandler(this, &CExercise::FormLoader);
}

void CExercise::FormLoader(Object ^ sender, EventArgs ^ e)
{       
	array<String ^> ^ FullNames = gcnew array<String ^>(8);

    FullNames[0] = L"Gertrude Monay";
    FullNames[1] = L"Paul Bertrand Yamaguchi";
    FullNames[2] = L"Hermine Ngaleu";
    FullNames[3] = L"Francine Mukoko";
    FullNames[4] = L"Joseph Walters";
    FullNames[5] = L"Patricia Katts";
    FullNames[6] = L"Helen Cranston";
    FullNames[7] = L"Paul Motto";

    CEnumerable ^ coll = gcnew CEnumerable;

    coll->Identify(FullNames);
    for each(String ^ s in coll)
        lbxNames->Items->Add(s);
}

int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
					 LPSTR lpCmdLine, int nCmdShow)
{
	Application::Run(gcnew CExercise());
	return 0;
}

IEnumerator, IEnumerable, and foreach

Choosing an Interface

While the IEnumerator and the IEnumerable interfaces serve as valuable accessories that allow a collection class to support enumeration, to actually create a collection class, there are other interfaces you can use to implement the functionality you want for your collection.

When you want to use a collection in your application, you may first check what classes are available in the .NET Framework. If you don't find a suitable class, you can create your own that implements one or more interfaces. As it happens, the .NET Framework ships with many of them and your next step is to choose which one you prefer. Some of the most commonly used interfaces are

  • System::Collections::IComparer and System::Collections::Generic::IComparer: If you derive a class from this interface, you can define how two objects would be compared for similarity or difference
  • System::Collections::IDictionary and System::Collections::Generic::IDictionary: This interface is used to create a collection class where each item is made of a  key=value combination
 

 

 

The ICollection Interface

 

Introduction

One of the primary pieces of information you should provide about the values in a collection is the number of items that a list is (currently) holding. When creating a collection class, to prepare it to provide this valuable information, you can (should) implement an interface named ICollection. The ICollection interface is defined in the System::Collections namespace while its equivalent of the same name is defined in the System::Collections::Generic namespace. This means that, if you are creating a class that implements it, you should include this namespace in the file. Here is an example of starting a class that implements the System::Collections::ICollection interface:

#pragma once
using namespace System::Collections;

public ref class CCollection : public ICollection
{

};

Implementing ICollection

To assist you with keeping track of the number of items in a collection, the ICollection interface is equipped with a property named Count, which you must implement. To do this, you can create a private member variable that will actually keep a count of the number of items. The Count property can then be used to communicate this information to the clients of the class. Here is an example:

Header File: Collection.h

#pragma once
using namespace System::Collections;

public ref class CCollection : public ICollection
{
private:
    int NumberOfBooks;

public:
    CCollection(void);

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

Source File: Collection.cpp

#include "Collection.h"

CCollection::CCollection(void)
    : NumberOfBooks(0)
{
}

The ICollection interface also allows its implementer to copy some of its items to an array. To provide this functionality, the interface is equipped with a method named CopyTo, which you must implement. The syntax of this method is:

void CopyTo(Array^ array, int index);

This method takes two arguments. The first argument is the array that will receive the items. The second argument is the index of the item from where the copying operation will begin. Here is an example:

Header File: Collection.h

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

public ref class CCollection : public ICollection
{
private:
    int NumberOfBooks;
    array<String ^> ^ books;

public:
    CCollection(void);

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

    virtual void CopyTo(Array ^ items, int index);
};

Source File: Collection.cpp

#include "Collection.h"

CCollection::CCollection(void)
    : NumberOfBooks(0)
{
}

void CCollection::CopyTo(Array ^ items, int index)
{
    array<String ^> ^ bks = gcnew array<String ^>(Count);

    for(int i = 0; i < Count; i++)
        bks[i] = books[i];
        
    items = bks;
}

If you create a collection class, you can provide the ability to enumerate its items. When this is done, some time to time, you will want to identify or to know what item is currently being accessed. In case other collection classes are using the same function at the time you are accessing this information, you should have an object that is responsible for synchronizing the collection. To do this in your ICollection-based class, you must implement a property named SyncRoot. This property must return an Object object. Here is an example:

Header File: Collection.h

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

public ref class CCollection : public ICollection
{
private:
    int NumberOfBooks;
    array<String ^> ^ books;

public:
	CCollection(void);

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

    virtual void CopyTo(Array ^ items, int index);

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

Besides the ability to specify the number of items in a collection, a class that implements the ICollection interface must retrieve a value that indicates whether its item is synchronized. To give this information, you must implement a Boolean property named IsSynchronized. Here is an example:

Header File: Collection.h

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

public ref class CCollection : public ICollection
{
private:
    int NumberOfBooks;
    array<String ^> ^ books;

public:
	CCollection(void);

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

    virtual void CopyTo(Array ^ items, int index);

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

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

System::Collections::ICollection and System::Collections::Generic::ICollection extend the IEnumerable interface. This means that you should be able to use for each in your ICollection-based class but you must create the functionality yourself, which is done by implementing the GetEnumerator() method. Even if you don't want to support this feature, you still must provide at least a skeleton for this method. Here is an example:

Header File: Collection.h

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

public ref class CCollection : public ICollection
{
private:
    int NumberOfBooks;
    array<String ^> ^ books;

public:
	CCollection(void);

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

    virtual void CopyTo(Array ^ items, int index);

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

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

    virtual IEnumerator ^ GetEnumerator();
};

Source File: Collection.cpp

#include "Collection.h"

CCollection::CCollection(void)
    : NumberOfBooks(0)
{
}

void CCollection::CopyTo(Array ^ items, int index)
{
    array<String ^> ^ bks = gcnew array<String ^>(Count);

    for(int i = 0; i < Count; i++)
        bks[i] = books[i];
        items = bks;
}

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

Home Copyright © 2009-2010 FunctionX, Inc. Next