Lesson 15: Constants, Fields, Properties and Indexers

Fields

Fields are variables associated with either classes or instances of classes. There are seven modifiers which can be used in their declarations. These include the four access modifiers 'public', 'protected', 'internal' and 'private' (discussed in lesson 12) and the 'new' keyword (discussed in lesson 14). The two remaining modifiers are:

static

By default, fields are associated with class instances. Use of the 'static' keyword, however, associates a field with a class. Where a field is declared in this way, the field will only ever have one value, regardless of the number of instances of the class in existence at any time.

readonly

Where a field is readonly, its value can be set only in a class declaration (for static fields) or in a constructor (for non-static fields).

Note that a field declaration can involve multiple fields, as in the following line of code

public static int a = 1, b, c = 2;

which is equivalent to

public static int a = 1;
public static int b;
public static int c = 2;

Constants

Constants are unchanging types, associated with classes, that are accessible at compile time. Because of this latter fact, constants can only be value types rather than reference types. Constant declarations take the 'const' keyword (not 'static', even though they are associated with classes), and the five modifiers 'public', 'protected', 'internal', 'private' and 'new'.

The following is a simple constant declaration, although multiple constants can be simultaneously declared.

public const int area = 4;

Properties

Properties can be thought of as 'virtual' fields. From the outside, a class' property looks just like a field. But from the inside, the property is generated using the actual class fields.

Property declarations take just those modifiers taken by methods (see lesson 13) Unlike languages like Java, C# provides dedicated support for accession and mutation of these properties. Suppose, for instance, that a type contains an internal field called 'age'. With the following code one could specify a property Age, providing accessors and mutators to this internal field.

1.

public int Age

2.

{

3.

    get

4.

    {

5.

        return this.age;

6.

    }

7.

    set

8.

    {

9.

        this.age = value;

10.

    }

11.

}


Notice that the term 'value' is used in the above piece of code. This variable always holds the value passed to the 'set' block. For instance, the execution of the following line of code (assuming the appropriate class instance) would automatically set 'value' in the 'set' block to 4.

person.Age = 4;

This property Age can be described as 'read-write' since it can be both read from and written to. To make a property 'write-only' one simply does not specify a 'get' block; to make it 'read-only' one does not specify a 'set' block. The following piece of code demonstrates the read-only property 'Adult':

1.

public boolean Adult

2.

{

3.

    get

4.

    {

5.

        if (this.age<18)

6.

            return false;

7.

        else

8.

            return true;

9.

    }

10.

}


Indexers

If properties are 'virtual fields', indexers are more like 'virtual arrays'. They allow a class to emulate an array, where the elements of this array are actually dynamically generated by function calls.

The following piece of code defines a class to hold a list of runners in an athletics race. The runners are held in lane order, and an indexer is exposed which allows the list to be both read from and written to. The indexer deals gracefully with cases in which the lane number passed to it is either too high or too low.

1.

class RaceDetails

2.

{

3.

    private string[] lanes;

4.

    

5.

    public RaceDetails()

6.

    {

7.

        this.lanes = new string[8];

8.

    }

9.

    

10.

    public string this[int i]

11.

    {

12.

        get

13.

        {

14.

            (i>=0 && i<8) ? return(this.lanes[i]) : return("error");

15.

        }

16.

        

17.

        set

18.

        {

19.

            if (i>=0 && i<8) this.lanes[i] = value;

20.

        }

21.

    }

22.

}


The following simple code illustrates use being made of the class just defined. The name of the person in the race's first lane is set, and then this name is sent to a console window.

1.

RaceDetails rd = new RaceDetails();

2.

rd[1] = "fred";

3.

Console.WriteLine("Lane One : " + rd[1]);


As can be seen from the example, an indexer is defined in a similar way to a property. One important difference is in the indexer's signature; the word 'this' is used in place of a name, and after this word indexing elements are provided.

Indexers aren't differentiated by name, and a class cannot declare two indexers with the same signature. However, this does not entail that a class is limited to just one indexer. Multiple indexing elements can be given, like parameters to methods (the differences being that with indexers at least one indexing element must be provided, and the 'ref' and 'out' modifiers cannot be used).

Because the indexing elements are not limited to integers, the original description of indexers as 'virtual arrays' actually rather undersells them. For example, where the indexing elements include strings, indexers will present themselves more like hash tables. Had the RaceDetails class been set up with such an indexer, code used to manipulate instances of the class might have looked like the following:

1.

RaceDetails rd = new RaceDetails();

2.

rd["fred"] = 1;

3.

Console.WriteLine("Fred is in : " + rd["fred"]);


 

C# Tutorial