Properties and Indexers in Depth

All the object-oriented languages support encapsulation, inheritance and polymorphism. What is different about C# is that it supports additional features like properties, events, indexers, delegates, etc. That is why C# is often termed as a modern object-oriented (OO) language. In this article we will discuss two modern OO concepts—properties and indexers.

Properties
Properties provide an interface for the data members of a class. Using properties, values can be stored and retrieved from the data members. So, what is the big deal about that? Even methods do the same thing. What’s good here is that we can access a property as if we are accessing a data member. Manipulating data using properties is done through get and set accessors. In the following example we have defined a property Size for a data member size.

class sample
{
            private int size ;
            public int Size
            {
                        get
                        {
                                     return size ;
                        }
                        set
                        {
                                     if ( value > 10 )
                                                 Console.WriteLine ( “Invalid Size” ) ;
                                     else
                                                 size = value ;
                        }
    }
}

The get accessor is invoked when we retrieve the value of the property, whereas the set accessor is invoked when we assign a value to the property. For example,

            sample s = new    sample( ) ;
            s.Size = 10 ;
            int i = s.Size ;

Here, the statement s.Size = 10 would invoke the set accessor. The value 10 would get collected in value. value is a keyword and is an implicit parameter of the set accessor. In the set accessor we have assigned the contents of value to the size data member. Before that we have validated the value assigned by the user to the property. Thus a property gives the syntactical convenience of a data member as well as the functionality of a method. We can write any program logic and throw exceptions in a property. It can be overridden and can be declared with any modifiers. The statement int i = s.Size would invoke the get accessor which returns the value of the size data member. The returned value would get collected in i.

Generally a property is associated with a data member. But this is not necessary. The get accessor can return a value without accessing a data member. For example, we can write a get property that returns system time without using a data member. The data type of a property can be a preliminary data type or a user-defined data type.

Indexers
An indexer is a new syntax that allows us to use an object as if the object itself is an array. This syntax is used if the class contains an array as a data member. An indexer allows access to the array elements within the class using the [ ] operator with an object. In one sense, we overload the [ ] operator. Unlike arrays, indexers allow usage of non-integer subscripts as well. Below, we see a program in which an indexer takes a string as a subscript.

using System ;
namespace stringindexer
{
   class sample 
   {
                        string[ ] english = new string[ ] {“Sunday”, “Monday”, “Tuesday”,
“Wednesday”, “Thursday”, “Friday”, “Saturday”} ;
                        string[ ] hindi = new string[ ] {“Itwar”, “Somwar”, “Mangalwar”,
“Budhwar”, “Guruwar”, “Shukrawar”, “Shaniwar”} ;
            public string this [string c]
            {
                        get
                        {
                                     for ( int i = 0 ; i < english.Length ; i++ )
                                                 {
                                                             if ( String.Compare ( c, english [ i ],
 true ) == 0 )
                                                             return hindi [ i ] ;
                                                 }
                                                 return “Not Found”;
                                     }
                                     set
                                     {
                                                 for ( int i = 0 ; i < english.Length ; i++ )
                                                 {
                                                             if ( String.Compare ( c, english [ i ],
 true ) == 0 )
                                                             {
                                                                         hindi [ i ] = value ;
                                                                         break ;
                                                             }
                                                 }
                                     }
                        }
   }
 
            class Class1
   {
                        static void Main ( string[ ] args )
                        {
                                     sample s = new sample( ) ;
                                     Console.WriteLine ( s[“Monday”] ) ;
                                     s[“Monday”] = “Som” ;
                                     Console.WriteLine ( s[“Monday”] ) ;
                        }
   }
}

In this example, the user would provide a string representing a weekday in English. The get accessor of the indexer would return the corresponding name in Hindi, whereas, set accessor would store the specified name in Hindi for the corresponding weekday in English. For example, s[“Monday”] should return “Somwar”. Similarly, s[“Monday”] = “Som” should set “Som” as Monday’s equivalent Hindi name. To implement this, we have declared two arrays of strings—english and hindi as data members of class sample. In Main( ), we have instantiated an object of sample and called the get accessor through the statement Console.WriteLine ( s [ “Monday” ] ) ;

“Monday” would get collected in c in the get accessor. If the specified weekday is available in the english array we have returned its Hindi equivalent from the hindi array. We have done a case insensitive comparison by mentioning true as a third parameter of the Compare( ) method.

Next, we have called the set accessor through the statement s [ “Monday” ] = “Som” ;

The string “Monday” would get collected in c. We have stored the name “Som”, which is available in the implicit parameter value, in the hindi array.

Indexers are nameless. It means that unlike properties we cannot assign name to an indexer. We use the this reference for writing indexers. Indexers may be overloaded. We can write one indexer for a 1-D array and another for a 2-D array in the same class. Needless to say, a 2-D indexer is accessed using [ , ] syntax.

Although indexers allow us to use an object as an array, we cannot use it in a foreach loop like an array. This is because foreach works only with collections. Writing an indexer does not qualify a class to be a collection.

It is not possible to write indexers for jagged arrays because a statement such as public int this [ int index1] [ int index2 ] results in an error.

Lastly, indexers cannot be declared as static because of the simple reason that the this reference is not available in static methods.