Previous Page
Next Page

What Are Properties?

A property is a cross between a logical field and a physical method. You use a property in exactly the same way that you use a field. Logically, a property looks like a field. However, the compiler automatically translates this field-like syntax into calls to special method-like accessors. A property declaration looks like this:

AccessModifier
Type PropertyName
{
    get
    {
        // read accessor code
    }

    set
    {
        // write accessor code
    }
}

A property can contain two blocks of code, starting with the get and set keywords. The get block contains statements that execute when the property is read, and the set block contains statements that run when the property is written to. The type of the property specifies the type of data read and written to by the get and set accessors.

The next code segment shows the ScreenPosition struct rewritten by using properties. When reading this code, notice the following:

TIP
The fields and properties follow the standard Microsoft Visual C# public/private naming convention. Public fields and properties should start with an uppercase letter but private fields and properties should start with a lowercase letter.
struct ScreenPosition
{
    public ScreenPosition(int X, int Y)
    {
        this.x = rangeCheckedX(X);
        this.y = rangeCheckedY(Y);
    }

    public int X
    {
        get { return this.x; }
        set { this.x = rangeCheckedX(value); }
    }

    public int Y
    {
        get { return this.y; }
        set { this.y = rangeCheckedY(value); }
    }

    private static int rangeCheckedX(int x) { ... } 
    private static int rangeCheckedY(int y) { ... } 
    private int x, y;
}

In this example, a private field directly implements each property. This is only one way to implement a property. All that is required is that a get accessor returns a value of the specified type. Such a value could easily be calculated, in which case there would be no need for a physical field.

NOTE
Although the examples in this chapter show how to define properties for a struct, they are equally applicable to classes; the syntax is the same.
Using Properties

When you use a property in an expression, you use it either in a read context (when you are not modifying its value) or in a write context (when you are modifying its value). The following example shows how to read values from the X and Y properties of a ScreenPosition struct:

ScreenPosition origin = new ScreenPosition(0, 0);
int xpos = origin.X;
int ypos = origin.Y;

Notice that you access properties and fields by using the same syntax. When you use a property in a read context, the compiler automatically translates your field-like code into a call to the get accessor of that property. Similarly, if you use a property in a write context, the compiler automatically translates your field-like code into a call to the set accessor of that property:

origin.X = 40;
origin.Y = 100;

The values being assigned are passed in to the set accessors by using the value variable, as described earlier. The runtime does this automatically.

It's also possible to use a property in a read/write context. In this case, both the get accessor and the set accessor are used. For example, the compiler automatically translates statements such as the following into calls to the get and set accessors:

origin.X += 10;
TIP
You can declare static properties, in the same way that you can declare static fields and methods. Static properties are accessed by using the name of the class or struct rather than an instance of the class or struct.
Read-Only Properties

You're allowed to declare a property that contains only a get accessor. In this case, you can use the property only in a read context. For example, here's the X property of the ScreenPosition struct declared as a read-only property:

struct ScreenPosition
{
    ...
    public int X
    {
        get { return this.x; }
    }
}

The X property does not contain a set accessor; therefore, any attempt to use X in a write context will fail. For example:

origin.X = 140; // compile-time error
Write-Only Properties

Similarly, you're allowed to declare a property that contains only a set accessor. In this case, you can use the property only in a write context. For example, here's the X property of the ScreenPosition struct declared as a write-only property:

struct ScreenPosition
{
    ...
    public int X
    {
        set { this.x = rangeCheckedX(value); }
    }
}

The X property does not contain a get accessor; any attempt to use X in a read context will fail. For example:

Console.WriteLine(origin.X); // compile-time error
origin.X = 200;              // compiles ok
origin.X += 10;              // compile-time error
NOTE
Write-only properties are useful for secure data such as passwords. Ideally, an application that implements security should allow you to set your password but should never allow you to read it back. A login method should only compare a user-supplied string with the stored password, and return an indication of whether they match.
Property Accessibility

The accessibility of a property (public, private, or protected) is specified when you declare the property. However, it is possible to specify different accessibilities for get and set accessors. For example, the version of the ScreenPosition struct shown below defines the set accessors of the X and Y properties as private (the get accessors remain public):

struct ScreenPosition
{
    ...
    public int X
    {
        get { return this.x; }
        private set { this.x = rangeCheckedX(value); }
    }

    public int Y
    {
        get { return this.y; }
        private set { this.y = rangeCheckedY(value); }
    }
    ...
    private int x, y;
}

You must observe some rules when defining accessors with different accessibility from each other:


Previous Page
Next Page