Click here to Skip to main content
15,908,175 members
Articles / Programming Languages / C#
Article

Three C# Tips: Indexed properties, property delegates, and read-only subproperties

Rate me:
Please Sign up or sign in to vote.
3.61/5 (19 votes)
31 May 20044 min read 176.9K   38   24
How to get indexed properties, property delegates, and readonly subproperties in C#.

Introduction

In various C# projects that I have worked on, I have found four features that I particularly wished were part of the C# language. These are indexed properties, property delegates, read-only subproperties and an internal access modifier inside interfaces. The three tips in this article show how to access three of these features in C# without specific language support.

Tip #1 Indexed Properties

Managed C++ supports indexed properties, so I was surprised to find them missing in C#. To achieve indexed properties without C# support, create an extra class to act as an indexer for the property. Here is a simple example.

C#
public class Class1
{
    public class ArrayIndexer
    {
        private Class1 arrayOwner;

        public ArrayIndexer(Class1 arrayOwner)
        {
            this.arrayOwner = arrayOwner;
        }

        public int this[int index]
        {
            get { return arrayOwner.localArray[index]; }
        }

        public int Length
        {
            get { return arrayOwner.localArray.Length; }
        }
    }

    private int[] localArray;

    public Class1()
    {
        localArray = new int[10];
        for (int i = 0; i < 10; i++)
            localArray[i] = i + 1;
        arrayIndexer = new ArrayIndexer(this);
    }

    public ArrayIndexer Numbers
    {
        get { return arrayIndexer; }
    }
    private ArrayIndexer arrayIndexer;
}

The ArrayIndexer class stores a reference to Class1 instead of a reference to localArray. This is intentional so that the indexer doesn't have to be updated every time a new array replaces the old. Also notice that each object in the array is read-only since only the getter is defined.

Given Class1, you can write the following code:

C#
Class1 a = new Class1();
int[] y = new int[10];
for (int i = 0; i < 10; i++)
    y[i] = a.Numbers[i];

So, why do this instead of just exposing the array as a property? There are at least two good reasons. First, if you expose the array directly, you are exposing your implementation. If not, then the internal representation has to be converted (or copied) to an array. This conversion would make the Numbers property look something like the code below (given an ArrayList called list, instead of the array localArray previously used):

C#
public int[] Numbers
{
    get
    {
        return list.ToArray(typeof(int));
    }    
}

If the programmer then naively writes the previous for loop, an array is created each time the loop is executed. To avoid this, he/she should write the code below:

C#
Class1 a = new Class1();
int[] x = a.Numbers;
int[] y = new int[10];
for (int i = 0; i < 10; i++)
    y[i] = x[i];

This requires the programmer to think about what is happening, obscures where the data is really coming from, and creates an extra, unnecessary object.

Tip #2 Property Delegates

A native C# property delegate would allow you to declare delegates for properties directly. Since they don't exist, here are two ways to create property delegates given the delegate and property definition below (this example only shows a delegate for the property setter):

C#
public delegate void MySetDelegate(object obj);

public int X
{
    get { return x; }
    set { x = value; }
}
private int x;

Solution 1 - wrap the property with a method. This is the safest way to create property delegates since it doesn't rely on C#'s internal naming convention for properties.

C#
private void xSet(int i)
{
    X = i;
}
.
.
.
private MySetDelegate xSetter = new MySetDelegate(xSet);

Solution 2 - use the Delegate class. This scheme relies on the C# internal naming convention for property getters and setters. Mistyped property names won't be found until runtime.

C#
private MySetDelegate xSetter = (MySetDelegate)Delegate.CreateDelegate(
    typeof(MySetDelegate), this, "set_X"
);

Tip #3 Read-only subproperties

The code below shows how a Location property might be implemented:

C#
public struct Point
{
    public Point(int x, int y)
    {
        this.x = x;
        this.y = y;
    }

    public int X
    {
        get { return x; }
        set { x = value; }
    }
    private int x;

    public int Y
    {
        get { return y; }
        set { y = value; }
    }
    private int y;
}

public class A
{
    public Point Location
    {
        get { return location; }
        set
        {
            location = value;
            // localVariable.Location = location;
        }
    }
    private Point location;
}

Notice that if you have an instance of class A called a, you can write a.Location.X = 10. Directly setting X (a subproperty of Location) on the Location property won't execute the code represented by the commented code localVariable.Location = location. This is a problem. There are two ways to deal with this. One is to make X and Y read-only properties in the Point structure. However, if you do this, nobody can set the properties except through constructors or methods. This is the approach taken with the GDI+ Color structure although there is much more to that structure than there is to Point.

The other way to deal with this problem is to create a read-only interface for Point. By declaring the type of the Location property in class A to be this interface, say IPoint, you make the subproperties of Location read-only. This is shown below.

C#
public interface IPoint
{
    int X { get; }
    int Y { get; }
}

public struct Point : IPoint
{
    public Point(int x, int y)
    {
        this.x = x;
        this.y = y;
    }

    public int X
    {
        get { return x; }
        set { x = value; }
    }
    private int x;

    public int Y
    {
        get { return y; }
        set { y = value; }
    }
    private int y;
}

public class A
{
    public IPoint Location
    {
        get { return location; }
        set
        {
            location.X = value.X;
            location.Y = value.Y;
            // OR
            location = (Point)value;

            // localVariable.Location = location;
        }
    }
    private Point location;
}

Conclusion

That has covered three out of four features in my wish list. What about the internal access modifier inside an interface? I don't have an answer yet. It would be a useful feature because I want an interface for internal use only, but anything inside that interface must be public. That means that any class used in the interface must be public! But, I want my class to be internal because it is only used internally! Oh, well. Perhaps the next version of C# will contain some of these features.

I hope this article returns to The Code Project community a tiny bit of the wealth which I have received from it.

References

History

  • June 1, 2004 - Version 1.0

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Software Developer (Senior) SL Corporation
United States United States
Daniel Barr is a Senior Software Engineer at SL Corporation(R) (www.slgms.com). SL Corporation(R) produces a number of monitoring solutions with graphical displays as well as the SL-GMS(R) graphical modeling solution. Daniel has worked as a software engineer since 1983.

Comments and Discussions

 
GeneralGeneric Delegates Pin
mke2229-Jun-10 2:40
mke2229-Jun-10 2:40 
GeneralMy vote of 2 Pin
mungflesh12-Jun-09 5:50
mungflesh12-Jun-09 5:50 
GeneralConfusing! Pin
Dmitri Nеstеruk5-Nov-08 0:42
Dmitri Nеstеruk5-Nov-08 0:42 
RantRe: Confusing! Pin
Daniel Ward14-Nov-08 15:46
Daniel Ward14-Nov-08 15:46 
GeneralArrayIndexer / Array Cloning [modified] Pin
Andrew Shapira16-Nov-06 14:58
Andrew Shapira16-Nov-06 14:58 
GeneralProperty Delegates Pin
Alois Kraus26-Feb-06 12:18
Alois Kraus26-Feb-06 12:18 
GeneralRe: Property Delegates Pin
barrd28-Feb-06 14:04
professionalbarrd28-Feb-06 14:04 
GeneralRe: Property Delegates [modified] Pin
Wes S31-Jan-07 9:57
Wes S31-Jan-07 9:57 
GeneralArrayIndexer circular references Pin
volox27-Jan-05 14:48
volox27-Jan-05 14:48 
Questionwhat about custom events? Pin
wickerman.2616-Jun-04 10:30
wickerman.2616-Jun-04 10:30 
AnswerRe: what about custom events? Pin
barrd16-Jun-04 21:50
professionalbarrd16-Jun-04 21:50 
GeneralRegarding read-only subproperties Pin
Angelos Petropoulos10-Jun-04 0:39
Angelos Petropoulos10-Jun-04 0:39 
GeneralRe: Regarding read-only subproperties Pin
barrd10-Jun-04 5:37
professionalbarrd10-Jun-04 5:37 
GeneralRe: Regarding read-only subproperties Pin
Angelos Petropoulos10-Jun-04 13:21
Angelos Petropoulos10-Jun-04 13:21 
GeneralRe: Regarding read-only subproperties Pin
barrd10-Jun-04 14:56
professionalbarrd10-Jun-04 14:56 
Generaluse class, not struct Pin
Lacuna27-Jun-04 18:57
Lacuna27-Jun-04 18:57 
GeneralRe: use class, not struct Pin
barrd28-Jun-04 10:21
professionalbarrd28-Jun-04 10:21 
GeneralRe: Regarding read-only subproperties Pin
Will Gray8-Feb-06 11:08
Will Gray8-Feb-06 11:08 
GeneralSorry.. Pin
Rocky Moore7-Jun-04 21:52
Rocky Moore7-Jun-04 21:52 
GeneralRe: Sorry.. Pin
barrd8-Jun-04 8:02
professionalbarrd8-Jun-04 8:02 
GeneralRe: Sorry.. Pin
Rocky Moore8-Jun-04 10:35
Rocky Moore8-Jun-04 10:35 
GeneralGreat work Pin
Uroš Šmon7-Jun-04 20:35
professionalUroš Šmon7-Jun-04 20:35 
GeneralRe: Great work Pin
barrd8-Jun-04 7:51
professionalbarrd8-Jun-04 7:51 
GeneralRe: Great work Pin
Muhahahahahahahahahahahaha7-Apr-07 22:03
Muhahahahahahahahahahahaha7-Apr-07 22:03 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.