Creating Indexed Properties in C#

May 11, 2012 | By Rakhitha | Filed in: .Net Stuff, Tech.

Indexers is a neat little feature that is available for objects in C#.  But it would have been even neater same support was there for properties as well. But all is not lost because if you spend little time getting your hands dirty you can actually implement indexed properties. All you got to do is to have your property read only and make sure it return an object which has an indexer. But if you get your hands little more dirty you can implement a generic helper for creating indexed properties.

Here we go…

IndexedProperty Helper Class

As a I said earlier the indexed property simply return an object of a class which has an indexer. And the implementation of the indexer should delegate the getter and setter calls back to the original object which own the property. Here is a sample implementation for the indexer object. I have used generics to make the implementation independent from data type of the property and the data type of index key.

 

    //This is the class for indexed property
    //this class simply has an indexer
    //and the indexer will delegate the setter and getter calls
    //back to parent object using two delegates
    //Generics are used to avoid having to hard code the indexer key and value types
    class IndexedProperty<IK,IV>
    {
        //These two delegates are used to delegate the get and set calls
        public delegate IV GetterDelegate<IKD, IVD>(IKD key);
        public delegate void SetterDelegate<IKD, IVD>(IKD key, IVD value);

        //Following variables will stor the actual delegate references
        GetterDelegate<IK, IV> getter;
        SetterDelegate<IK, IV> setter;

        //Constructor
        //When creating the object you need to provide the referances to getter and setter methods
        //which will be called ultimately when the indexer is used
        public IndexedProperty(GetterDelegate<IK, IV> getMethod, SetterDelegate<IK, IV> setMethod)
        {
            this.getter = getMethod;
            this.setter = setMethod;
        }

        //The indexer
        //Nothing much here
        //Just pass the call to the relevant gelegate
        public IV this[IK index]
        {
            get
            {
                return getter(index);
            }
            set
            {
                setter(index,value);
            }
        }
    }

In the above code as you can see there is no much magic. The main part of the code is in the indexer implemented in line 28. It just call the relevant delegate. Two delegates are initialized at the constructor using the parameters passed by the creator. Therefore its the creator who is responsible for actually setting and getting the value of whatever the indexed content.

Using IndexedProperty Helper Class

Following code demonstrates how the above class is used. The given example is a very theoretical one and it could have been done using collection classes in .Net framework. But it is implemented using IndexedProperty class just to demonstrate how it is used. Following implementation is just a map which stores key/value pairs of string data. But in a real scenario you will use this in cases where data is not directly stored in an indexed collection (in that case you can directly make use of the indexer of that collection) but the data is accessed in an indexed manner.

In the following code the indexed property (named Item) is simply a property of type IndexedProperty. They IndexedProperty  instance that we return is initialized with the delegates to get_value and set_value methods which does the actual work.

    //This is just an example of how to use the IndexedProperty Class
    //In this case I have implemented an structure similer to a Perl Hash
    class ClassWithIndexedProperty
    {
        //This is the variable in which we keep the data
        //een numbered elements will contail the keys while
        //odd numbered elements contaim values
        string[] indexedData = new string[100];

        //The object for indexer property
        //in this example both key and value are of type string
        IndexedProperty<string, string> CharIndexer;

        public ClassWithIndexedProperty()
        {

        }

        //This is the method for setter
        private void set_value(string index, string value)
        {
            int nullIndex = -1;
            for (int i = 0; i < indexedData.Length; i += 2)
            {
                if (indexedData[i] == null && nullIndex < 0)
                    nullIndex = i;
                if (indexedData[i] == index)
                {
                    indexedData[i + 1] = value;
                    return;
                }
            }
            if (nullIndex >= 0)
            {
                indexedData[nullIndex] = index;
                indexedData[nullIndex + 1] = value;
            }
            else {
                //Throw an Exception
            }
        }

        //This is the method for getter
        private string get_value(string index)
        {
            for (int i = 0; i < indexedData.Length; i += 2)
            {
                if (indexedData[i] == index)
                {
                    return indexedData[i + 1] ;
                }
            }
            return null;
        }

        //Here comes the indexed property
        //Return type is IndexedProperty which implements indexer
        public IndexedProperty<string, string> Item
        {
            get
            {
                //When we access the property for the first time we create the object
                //And in subsequent calls we just need to return it.
                if (CharIndexer == null)
                    CharIndexer = new IndexedProperty<string, string>(get_value, set_value);
                return CharIndexer;
            }
        }
    }

Even though its 70 lines long, in above example the only part you need to focus here is the actual implementation of the property named “Item” in lines 58 to 67. There we simply return an object of IndexedPrperty which is properly initialized with delegates to methods which does actual work.

Testing

Following is just a simple test program that I wrote to test above code.

    class Program
    {
        static void Main(string[] args)
        {
            ClassWithIndexedProperty test = new ClassWithIndexedProperty();
            test.Item["A"] = "Apple";
            test.Item["B"] = "Banana";

            Console.WriteLine(test.Item["A"]);
        }
    }

To Do…

Read Only and Write Only Indexed Properties

As you can see in the actual indexer in IndexedProperty class it simply pass the call to a delegate. The creator may choose to pass null as one of the delegates to make it read only or write only. Therefore its good to handle the null value inside the indexer and throw a proper exception.

Multi-Dimensional Keys

Given IndexedProperty class handle any data type as long as the key is one dimensional. You will need to change some code to support multi-dimensional keys.

Any Suggestions?


Tags: , , , , ,