Most of the time, you’ll want to designate the member variables of a class as private
. This means that only member methods of that class can access their value. When you prevent methods outside the class from directly accessing member variables, you’re enforcing data hiding, which is an aspect of the encapsulation of a class, as we discussed in Chapter 6.
That’s fine, but if the members are private, how do your other classes access that data? The answer for C# programmers is properties. Properties allow other methods (called clients) to access the state of your class as though they were accessing member fields directly, although you’re actually implementing that access through a class method.
This solution is ideal. The client wants direct access to the state of the object. As the class designer, though, you want to hide the internal state of the class in class fields and provide indirect access through a method. For example, you might want external classes to be able to read a value, but not change it; or you might want to write some code so that the internal field can accept only values in a certain range. If you grant external classes free access to your member fields, you can’t control any of that. The property provides both the illusion of direct access for the client and the reality of indirect access for the class developer.
By separating the class state from the method that accesses that state (a process called decoupling), you’re free to change the internal state of the object whenever you need to. When the Box
class is first created, the length
value might be stored as a member variable. Later on, you might redesign the class so that the length
value is computed or maybe retrieved from a database. If the client had direct access to the original length
member variable, changing how that value is resolved would break the client. By decoupling and forcing the client to go through a property, the Box
class can change how it manages its internal state without breaking client code.
In short, properties provide the data hiding required by good object-oriented design. Example 8-2 creates a property called length
, which is then discussed in the paragraphs that follow.
Example 8-2. Properties provide data hiding by supplying the client with a method that looks like the client is accessing the member variable directly
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Example_8_2_ _ _ _Properties { public class Box { // private variables private int length; private int width; private int height; // property public int Length { get { return length; } set { length = value; } } // public methods public void DisplayBox( ) { Console.WriteLine("Length: {0}, Width: {1}, Height: {2}", length, width, height); } // constructor public Box(int theLength, int theWidth, int theHeight) { length = theLength; width = theWidth; height = theHeight; } } public class Tester { public void Run( ) { // create a box for testing and display it Box testBox = new Box(3, 5, 7); testBox.DisplayBox( ); // access the length, store it in a local variable int testLength = testBox.Length; Console.WriteLine("Length of box is: {0}", testLength); // increment the length testLength++; // assign the new value to the member variable testBox.Length = testLength; // display the box again to test the new value testBox.DisplayBox( ); } static void Main( ) { Tester t = new Tester( ); t.Run( ); } } }
The output should look something like this:
Length: 3, Width: 5, Height: 7 Length of box is: 3 Length: 4, Width: 5, Height: 7
You create a property by writing the property type and name followed by a pair of braces. Within the braces, you can declare the get
and set
accessors. These accessors are very similar to methods, but they are actually part of the property itself. The purpose of these accessors is to provide the client with simple ways to retrieve and change the value of the private member length
, as you’ll see.
Neither of these accessors has explicit parameters, though the set
accessor has an implicit parameter called value
, which is used to set the value of the member variable.
By convention, property names are written in Pascal notation (initial uppercase), and member fields have initial lowercase. This isn’t mandatory, but it makes it easier to distinguish between the field and the property.
In Example 8-2, the declaration of the length
property creates both get
and set
accessors:
// property public int Length { get { return length; } set { length = value; } }
Each accessor has an accessor body, which does the work of retrieving or setting the property value. The property value might be stored in a database (in which case, the accessor would do whatever work is needed to interact with the database), or it might just be stored in a private member variable (in this case, length
):
private int length;
The body of the get
accessor (sometimes called a “getter”) is similar to a class method that returns an object of the type of the property. In Example 8-2, the accessor for the Length
property is similar to a method that returns an int
. It returns the value of the private member variable length
in which the value of the property has been stored:
get { return length; }
In this example, the value of a private int
member variable is returned, but you could just as easily retrieve an integer value from a database or compute it on the fly.
Remember, this description is from the perspective of the author of the Box
class. To the client (user) of the Box
class, Length
is a property, and how the Box
class returns its length is encapsulated within the Box
class—the client doesn’t know or care.
Whenever you need to retrieve the value (other than to assign to it), the get
accessor is invoked. For example, in the following code, the value of the Box
object’s Length
property is assigned to a local variable. You use the dot operator to call the accessor, exactly as you would if you were accessing a public property.
To the client, the local variable testLength
is assigned the value of the Length
property of testBox
(the Box
object). To the creator of the Box
object, however, the get
accessor is called, which in this case returns the value of the length
member variable:
Box testBox = new Box(3, 5, 7); int testLength = testBox.Length;
The set
accessor (sometimes called a “setter”) sets the value of a property. When you define a set
accessor, you must use the value
keyword to represent the argument whose value is assigned to the property:
set { length = value; }
Here, again, a private member variable is used to store the value of the property, but the set
accessor could write to a database or update other member variables as needed.
When external code assigns a value to the property, the set
accessor is automatically invoked, and the implicit parameter value is set to the value you assign:
testLength++; testBox.Length = testLength;
The first line increments a local variable named testLength
. As far as the client is concerned, that new value is assigned to the Length
property of the local Box
object testBox
. Again, this looks the same as though you were assigning to a public variable. To the author of the Box
class, however, the local variable testLength
is passed in to the set accessor as the implicit parameter value
and assigned (in this case) to the local member variable length
.
The advantage of this approach is that the client can interact with the properties directly, without sacrificing the data hiding and encapsulation sacrosanct in good object-oriented design.
Although we mentioned that the accessors may calculate values on the fly, or access a database to obtain a value, most of the time you’ll just use them to retrieve or set an internal member directly, like you saw with the Length
property:
public int Length { get { return length; } set { length = value; } }
As you can imagine, if you have a lot of private member fields in your class, creating accessors for all of them is both repetitive and mindless. Therefore, if all you’re doing is retrieving or setting a private member, you can use a shortcut syntax called automatic properties. That syntax works like this:
public int Length { get; set; }
Simple, isn’t it? This syntax will save you a lot of typing in complicated classes. Remember, though, that if you want to do anything other than simply retrieve or assign the value, you’ll need to create the accessor by hand. It’s also worth mentioning that if you use the automatic properties, you shouldn’t also create the private members; the compiler will do that for you behind the scenes.