A long-accepted tenet of object-oriented programming is that it’s a bad idea to give users direct access to the data members that make up your classes. There are two main reasons for this:
If users directly access data members, they’re required to know about the implementation of the class, and that might limit your ability to modify the implementation later.
Users of your classes might—deliberately or accidentally—corrupt the data in objects by using inappropriate values, possibly leading to program failures or other undesirable results.
As a result, it’s recommended that you hide data members, making them private and giving indirect access to them by using member functions. In traditional C++, indirect access has often been implemented using get and set members so that a data member named date might be accessed using a pair of member functions named set_date and get_date. This method works fine, but client code always has to call the get and set functions directly.
Properties in the .NET Framework give you a way to implement a virtual data member for a class. You implement the get and set properties, and the compiler converts them into calls to the get or set method as appropriate.
MyClass* pmc = new MyClass; pmc->Name = "fred"; // calls pmc->set_Name("fred") s = pmc->Name; // calls pmc->get_Name()
It appears to the user that MyClass has a real data member named Name, and the property can be used in exactly the same way as a real data member.
If you’ve programmed in Visual Basic, the idea of implementing properties using the get, set, and let methods should be familiar to you. In the .NET Framework, properties can be created and used in any .NET language, so you can create a class in Visual Basic and still use its properties in a C++ program, and vice versa.
Two kinds of properties are supported by the Managed Extensions for C++: scalar properties and indexed properties.
A scalar property gives access to a single value by using get and set methods. For example, a Name property would implement the get_Name and set_Name functions to give access to the underlying name data. It’s important to note that a property doesn’t have to represent a simple data member of the managed class. A property can represent derived values. For example, if a class has a date of birth member, it would be possible to implement an age property that calculates the age. Properties can also represent far more complex values, which might involve using data from other sources, such as searching databases or accessing URLs.
An indexed property allows a property to be accessed as if it’s an array, using the traditional C++ square bracket () notation.
If you’ve ever come across the overloaded [ ] operator in traditional C++, you’ll find that indexed properties provide similar functionality, but you don’t have to code the operator overload yourself.
Indexed properties still use the get and set methods for implementation, and the compiler automatically generates the required code so that clients can use the square bracket notation. So, if the compiler sees a property that can be implemented as an indexed property, it will automatically generate the code.
The next sections in this chapter demonstrate how to implement both scalar and indexed properties.