Easy creation of properties that support indexing in C#
c#, indexed-properties, properties
Solution
I found your idea useful, so I extended it. This may not technically be a proper answer since I'm not sure it squarely answers your question, but I thought it might be useful to people who came here looking for property indexers.
First, I needed to be able to support get-only and set-only properties, so I made a slight variation of your code for these scenarios:
Get and Set (very minor changes):
public class IndexedProperty<TIndex, TValue>
{
readonly Action<TIndex, TValue> SetAction;
readonly Func<TIndex, TValue> GetFunc;
public IndexedProperty(Func<TIndex, TValue> getFunc, Action<TIndex, TValue> setAction)
{
this.GetFunc = getFunc;
this.SetAction = setAction;
}
public TValue this[TIndex i]
{
get
{
return GetFunc(i);
}
set
{
SetAction(i, value);
}
}
}
Get Only:
public class ReadOnlyIndexedProperty<TIndex, TValue>
{
readonly Func<TIndex, TValue> GetFunc;
public ReadOnlyIndexedProperty(Func<TIndex, TValue> getFunc)
{
this.GetFunc = getFunc;
}
public TValue this[TIndex i]
{
get
{
return GetFunc(i);
}
}
}
Set Only:
public class WriteOnlyIndexedProperty<TIndex, TValue>
{
readonly Action<TIndex, TValue> SetAction;
public WriteOnlyIndexedProperty(Action<TIndex, TValue> setAction)
{
this.SetAction = setAction;
}
public TValue this[TIndex i]
{
set
{
SetAction(i, value);
}
}
}
Example
Here's a simple usage example. I inherit from Collection and create a named indexer, as Jon Skeet called it. This example is intended to be simple, not practical:
public class ExampleCollection<T> : Collection<T>
{
public ExampleCollection()
{
ExampleProperty = new IndexedProperty<int, T>(GetIndex, SetIndex);
}
public IndexedProperty<int, T> ExampleProperty { get; }
private T GetIndex(int index)
{
return this[index];
}
private void SetIndex(int index, T value)
{
this[index] = value;
}
}
ExampleCollection in the Wild
This hastily constructed unit test shows how it looks when you ExampleCollection in a project:
[TestClass]
public class IndexPropertyTests
{
[TestMethod]
public void IndexPropertyTest()
{
var MyExample = new ExampleCollection<string>();
MyExample.Add("a");
MyExample.Add("b");
Assert.IsTrue(MyExample.ExampleProperty[0] == "a");
Assert.IsTrue(MyExample.ExampleProperty[1] == "b");
MyExample.ExampleProperty[0] = "c";
Assert.IsTrue(MyExample.ExampleProperty[0] == "c");
}
}
Finally, if you want to use the get-only and set-only versions, that looks like this:
public ExampleCollection()
{
ExampleProperty = new ReadOnlyIndexedProperty<int, T>(GetIndex);
}
public ReadOnlyIndexedProperty<int, T> ExampleProperty { get; }
Or:
public ExampleCollection()
{
ExampleProperty = new WriteOnlyIndexedProperty<int, T>(SetIndex);
}
public WriteOnlyIndexedProperty<int, T> ExampleProperty { get; }
In both cases, the result works the way you would expect a get-only/set-only property to behave.
Problem
In C# I find indexed properties extremely useful. For example: ``` var myObj = new MyClass(); myObj[42] = "hello"; Console.WriteLine(myObj[42]); ``` However as far as I know there is no syntactic sugar to support fields that themselves support indexing (please correct me if I am wrong). For example: ``` var myObj = new MyClass(); myObj.field[42] = "hello"; Console.WriteLine(myObj.field[42]); ``` The reason I need this is that I am already using the index property on my class, but I have `GetNumX()`, `GetX()`, and `SetX()` functions as follows: ``` public int NumTargetSlots { get { return _Maker.NumRefs; } } public ReferenceTarget GetTarget(int n) { return ReferenceTarget.Create(_Maker.GetReference(n)); } public void SetTarget(int n, ReferenceTarget rt) { _Maker.ReplaceReference(n, rt._Target, true); } ``` As you can probably see exposing these as one indexable field property would make more sense. I could write a custom class to achieve this every time I want the syntactic sugar but all of the boilerplate code just seem unnecessary. So I wrote a custom class to encapsulate the boilerplate and to make it easy to create properties that can be indexed . This way I can add a new property as follows: ``` public IndexedProperty<ReferenceTarget> TargetArray { get { return new IndexedProperty<int, ReferenceTarget>( (int n) => GetTarget(n), (int n, ReferenceTarget rt) => SetTarget(n, rt)); } } ``` The code for this new IndexedProperty class looks like: ``` public class IndexedProperty<IndexT, ValueT> { Action<IndexT, ValueT> setAction; Func<IndexT, ValueT> getFunc; public IndexedProperty(Func<IndexT, ValueT> getFunc, Action<IndexT, ValueT> setAction) { this.getFunc = getFunc; this.setAction = setAction; } public ValueT this[IndexT i] { get { return getFunc(i); } set { setAction(i, value); } } } ``` So my question is: is there a better way to do all of this? Well to be specific, is there a more idiomatic way in C# to create an indexable field property, and if not how could I improve my `IndexedProperty` class? EDIT: After further research, Jon Skeet calls this a "named indexer".