views:

144

answers:

10

I want to create a property in C# that sets or returns an individual member of an array. Currently, I have this:

private string[] myProperty;
public string MyProperty[int idx]
{
    get
    {
        if (myProperty == null)
            myProperty = new String[2];

        return myProperty[idx];
    }
    set
    {
        myProperty[idx] = value;
    }
}

However, I get the following compile error:

Bad array declarator: To declare a managed array the rank specifier precedes the variable's identifier. To declare a fixed size buffer field, use the fixed keyword before the field type.

+5  A: 

You must use this as the property name for indexers.

leppie
What if I have more than one?
pm_2
You'll have to make MyProperty return some kind of indexable list. If you need tight control, you may want to implement it yourself.
recursive
A: 

You need to use an indexer. It works a little differently. See example:

public class Node
{
    public Node this[int offset]
    {
        get { return localList[offset]; }
    }
}

Note: You are allowed only one indexer per class. The reason is that it is too confusing to the compiler as to the meaning, so you only are allowed one.

You can also do this:

private static int[] _widget = new int[Counter];
public static int [] Widget
{
    get { return _widget; }
    set { _widget = value; }
}

...

for (int i = 0; i <  MyClass.Counter; i++)
{
    MyClass.Widget[i] = i;
}

...
double _newWidget5 = MyClass.Widget[5];
// and so on...
0A0D
+5  A: 

C# allows only one indexed property per class, so you are forced to use this.

joekoyote
that's not true. You can have more than one indexer so long as the signature is different, i.e. the type of what is used as the index value is different.
Matt Ellen
The things have changed then. Initially such limitation did exist. Would you mind posting new syntax and tell us when it was added to the language?
joekoyote
maybe we're talking at cross purposes: you can declare `public int this[string index]` along side `public int this[int index]` and they can do two completely different things. I would say that is more than one indexed property.
Matt Ellen
Got it. From class designer point of view you still have one property as it has one name. Imagine StringList class, which in other languages could have properties Strings[int], Objects[string key], Names[int], Values[string name] etc. In C# you can't implement them and even when you can, it's not obvious and almost unusable.
joekoyote
+1  A: 

An option is to recode it as follows:

private string[] myProperty = new string[2]; 
public string[] MyProperty
{ 
    get 
    { 
        return myProperty;
    } 
    set 
    { 
        myProperty = value; 
    } 
} 

It'll compile, but it does have its own set of issues (fxCop will yell about it, but it can lead you to other options).

Russ
This is stronger than the original interface, which only allow modification of the elements. This actually allows replacement of the underlying array via set.
Dan Bryant
+7  A: 

How about this: write a class that does one thing and one thing only: provide random access to elements of some underlying indexed collection. Give this class a this indexer.

For properties that you want to provide random access to, simply return an instance of this indexer class.

Trivial implementation:

public class Indexer<T>
{
    private IList<T> _source;

    public Indexer(IList<T> source)
    {
        _source = source;
    }

    public T this[int index]
    {
        get { return _source[index]; }
        set { _source[index] = value; }
    }
}

public static class IndexHelper
{
    public static Indexer<T> GetIndexer<T>(this IList<T> indexedCollection)
    {
        // could cache this result for a performance improvement,
        // if appropriate
        return new Indexer<T>(indexedCollection);
    }
}

Refactoring into your code:

private string[] myProperty;
public Indexer<string> MyProperty
{
    get
    {
        return myProperty.GetIndexer();
    }
}

This will allow you to have as many indexed properties as you want, without needing to expose those properties with the IList<T> interface.

Dan Tao
+1  A: 

You could do something like this:

class Indexers
{
    private string[] _strings = new [] {"A","B"};
    private int[] _ints = new[] { 1, 2 };

    public string[] Strings
    {
        get{ return _strings;}
    }

    public int[] Ints
    {
        get{ return _ints;}
    }
}

class Program
{
    static void Main(string[] args)
    {
        Indexers indexers = new Indexers();

        int a1 = indexers.Ints[0];
        string a2 = indexers.Strings[0];
    }
}
fletcher
+1  A: 

You can use it this way:

    private string[] myProp;
    public string[] MyProp
    {
        get
        {
            if (myProp == null)
            {
                myProp = new String[2];
            }
            return myProp;
        }

        set
        {
            myProp = value;
        }
    }

And it's possible to acces myProp[1] as MyProp[1] for Example

Tokk
+2  A: 

Exposing your array through a read-only property might cover your needs. Since you don't want to allow other code to assign the array as such, there is no need for a public setter:

private string[] myProperty;
public string[] MyProperty
{
    get
    {
        if (myProperty == null)
        {
            myProperty = new String[2];
        }

        return myProperty;
    }  
}

Then you can write code as such:

theObject.MyProperty[1] = "some string";

...but you cannot replace the array itself:

theObject.MyProperty = new string[2]; // will not compile
Fredrik Mörk
+1  A: 

First, in-field declaration avoids excess check:

private string[] myProperty = new string[2];

You can implement several indexers via overloading by input type:

public string this[int index]
{
    get
    {
        return myProperty[index];
    }
    set
    {
        myProperty[index] = value;
    }
}

public object this[object a, object b] // different input type(s) (and different return type)
{
    get
    {
        // do other stuff
    }
}
abatishchev
+1  A: 

C# provides no built-in mechanism to create indexed properties. You can use a class-level indexer (using this[int index] notation), but nothing like this on a property level.

One option is to create a helper class with an indexer and use this class as the property type. See an example on MSDN.

VladV