Encapsulating Data with Properties

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.

Tip

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 get Accessor

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.

Tip

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

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.

Tip

You can create a read-only property by not implementing the set part of the property. Similarly, you can create a write-only property by not implementing the get part.

Automatic Properties

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.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.147.45.90