HasA Relationships

In addition to is-a relationships (achieved via base classes) and behaves-as relationships (achieved via base interfaces), you can also define has-a relationships between managed types. A has-a relationship can be established simply by adding a field to a managed class.

For encapsulation reasons, it is not recommended to define public fields. However, it is sometimes desirable to expose has-a relationships to clients. To achieve this without using public fields, you can define properties. Properties consist of metadata and either one or two accessor methods. For read access to a property, a get accessor method is called. Write access is translated to a call of a set accessor method. A property can have either both accessor methods or just one of them. This means that you can define read-only properties and write-only properties, as well as properties that can be read and modified.

In most .NET languages, including C++/CLI, properties can be accessed with a field-like syntax. In the following code, the read-only property Length of a managed array is used:

void DumpArray(array<ObjectA>A arr) {

for (int i = 0; i < arr->Length; ++i) Console::WriteLine(arr[i]);

The next block of code shows a managed class, Person, with two properties, Name and Age. For the Name property, the accessors are implemented in the class declaration. The accessors of Age are just declared in the class and implemented outside. The setter of the Age property performs a consistency check; if the assigned value is less than 0 or greater than 150, a System::ArgumentException is thrown.

public ref class Person {

// backing storage for properties StringA name; int age;

public:

property StringA Name {

return name;

void set(StringA newValue) {

name = newValue;

property int Age {

// here, accessor methods are only declared, not implemented int get(); void set(int);

// implementation of Age's accessor methods int Person::Age::get() {

return age;

void Person::Age::set(int newValue) {

throw gcnew ArgumentException("newValue must be > 0 and < 150."); age = newValue;

For many property implementations, it is sufficient to define a simple backing storage field, a get accessor that returns the backing storage field, and a set accessor that updates the backing storage. C++/CLI simplifies the definition of such a property with the following trivial property syntax:

public ref class Person {

public: property StringA Name;

... definition of Age property not trivial ...

Properties can also be declared as virtual. This implies that the accessor functions of the property are virtual functions. In contrast to fields, properties can be members of interfaces.

To handle a one-to-w relationship, a property of a container type is typically used. In the simplest case, such a container type is a managed array, but the FCL comes with a bunch of other container types, too. In the namespace System::Collections, there are several container types. These types include ArrayList, Queue, Stack, SortedList, and HashTable. All these collections can be used to manage elements of type System::ObjectA. When you use these collections with concrete types, downcasts are necessary. These casts are either dangerous (static_cast) or expensive (safe_cast). When the element types for these collections are value types, using these collections also implies a lot of boxing and unboxing.

Version 2.0 of the FCL also contains the namespace System::Collections::Generic with typed collection classes. These classes are based on generics, a template-like .NET feature that allows you to define and use parameterized types. As an example, there is a type List that represents a typed list of a dynamic size. The following code uses the read-only List<PersonA> property to establish a one-to-n relationship:

using namespace System;

using namespace System::Collections::Generic;

public ref class Person {

Was this article helpful?

0 0

Post a comment