Easily create properties that support indexing in C #

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 because I already use the index property in my class, but I have the functions GetNumX() , GetX() and SetX() 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 probably see, they can be exposed because one property of the indexed field makes sense. I could write my own class to achieve this every time I want syntactic sugar, but all the boilerplate code just seems unnecessary.

So, I wrote a special class to encapsulate a template and to simplify the creation of properties that can be indexed. Thus, 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 is as follows:

 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 this ?

Well, to be specific, is there a more idiomatic way in C # to create an indexed field property, and if it weren’t for me, I could improve my IndexedProperty class?

EDIT: After further research, John Skeet calls this a named index .

+27
c # properties indexed-properties
Jul 27 2018-10-28T00:
source share
7 answers

Well, the easiest way to return a property is an object that implements IList .

Remember that just because it implements IList does not mean that it is a collection itself, it just implements certain methods.

+8
Jul 27 '10 at 14:30
source share

This is not a technically correct way to use StackOverflow, but I found your idea so useful that I expanded it. I thought it would save people if I publish what I came up with and an example of how to use it.

First, I needed to support the get-only and set-only properties, so I changed your code a bit for these scenarios:

Get and install (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); } } } 

Installation 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 is a simple use case. I inherit the collection and create a named indexer, as John Skeet called it. This example should be simple, impractical:

 public class ExampleCollection<T> : Collection<T> { public IndexedProperty<int, T> ExampleProperty { get { return new IndexedProperty<int, T>(GetIndex, SetIndex); } } 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 what it looks like 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 versions only for receiving and installing, it looks like this:

  public ReadOnlyIndexedProperty<int, T> ExampleProperty { get { return new ReadOnlyIndexedProperty<int, T>(GetIndex); } } 

Or:

  public WriteOnlyIndexedProperty<int, T> ExampleProperty { get { return new WriteOnlyIndexedProperty<int, T>(SetIndex); } } 

In both cases, the result works exactly as you expect it to have the get-only / set-only property.

+11
Jul 12 '13 at 15:46
source share

I think the design you posted is the way to go, with the difference that I would define an interface:

 public interface IIndexed<IndexT, ValueT> { ValueT this[IndexT i] { get; set; } } 

And for normal cases, I would use the class that you entered in the original question (which this interface will implement).

It would be nice if the base class library provided us with a suitable interface, but it is not. The return of IList here would be a perversion.

+7
Jul 27 '10 at 18:00
source share

This does not answer your question, but it is interesting to note that CIL supports the creation of such properties as you described - some languages ​​(for example, F #) will also allow you to define them in this way.

The this[] indexer in C # is just a concrete instance of one of them, which when you create your application is renamed to Item . The C # compiler knows how to read this, so if you write a “named indexer” called Target in the F # library and try using it in C #, the only way to access this property is through ... get_Target(int) and void set_Target(int, ...) . Sucks.

+4
Jul 27 '10 at 18:16
source share

Why don't you inherit the IList class, then you can just use the index and add your own properties to it. Although you still have the Add and Remove features, it is not dishonest not to use them. In addition, you may find it helpful to walk along the road.

Additional information about lists and arrays: What is better to use an array or List <gt ;?

EDIT:

MSDN has an article on index properties that you can look at. It seems to be not difficult, just tiring.

http://msdn.microsoft.com/en-us/library/aa288464(VS.71).aspx

There is another option when you can create an alternative Add method, but depending on the type of object, the add method may not always be called. Explained here:

How to override the method of adding List <T> in C #?

EDIT 2: As in the first post

Why don't you have a hidden list object in your class and then just create your own methods to get the data. Thus, adding and removing are not displayed, and the list is already indexed.

Also, what do you mean by "named indexer", you are looking for the equivalent of the string ["My_Column_Name"]. This is the MSDN article I found that may be useful as it shows the basic way to implement this property.

http://msdn.microsoft.com/en-us/library/146h6tk5.aspx

 class Test { private List<T> index; public T this[string name]{ get; set; } public T this[int i] { get { return index[i]; } set { index[i] = value; } } } 
+3
Jul 27
source share

After some research, I came up with a slightly different solution that better suited my needs. An example is a little thought up, but it is suitable for what I need in order to adapt it.

Using:

 MyClass MC = new MyClass(); int x = MC.IntProperty[5]; 

And the code to make it work:

 public class MyClass { public readonly IntIndexing IntProperty; public MyClass() { IntProperty = new IntIndexing(this); } private int GetInt(int index) { switch (index) { case 1: return 56; case 2: return 47; case 3: return 88; case 4: return 12; case 5: return 32; default: return -1; } } public class IntIndexing { private MyClass MC; internal IntIndexing(MyClass mc) { MC = mc; } public int this[int index] { get { return MC.GetInt(index); } } } } 
+2
Feb 24 '15 at 13:18
source share

Try explicitly implemented interfaces, as shown in the second path suggested in the answer here: Named indexed property in C #?

0
May 30 '16 at 3:16
source share



All Articles