'Custom Named Property Indexers

Is there any possible way to make a property that uses an indexer other than one global one for the whole class? Here's the gist of what I want to do. Note that in this example, you should pretend I cannot move data or create another Dictionary.

class MyClass
{
    protected Dictionary<string,object> data = new Dictionary<string,object>();
    public object Foo[int index]
    {
        get {
            string name = String.Format("foo{0}",index);
            return data[name];
        }
    }
    public object Bar[int index]
    {
        get {
            string name = String.Format("bar{0}",index);
            return data[name];
        }
    }
}

I would then be able to reference an item of the data named foo15 or bar12 like so

MyClass myClass = new MyClass();
Console.WriteLine(myClass.Foo[15]);
Console.WriteLine(myClass.Bar[12]);

I'm aware you can do this, but this isn't what I want.

    public object this[string index]
    {
        get {
            return data[index];
        }
    }


Solution 1:[1]

You can do it, but it requires jumping through some hoops. C# does not support Indexed Properties natively. I learned how using this post: Easy creation of properties that support indexing in C#

First declare an IndexedProperty class:

public class IndexedProperty<TIndex, TValue>
{
    Action<TIndex, TValue> setAction;
    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);
        }
    }
}

And here's one example of using it (the referenced post contains a simpler example):

    protected Dictionary<DialogResult, string> ButtonNames { get; set; }
    public IndexedProperty<DialogResult, string> ButtonName
    {
        get
        {
            return new IndexedProperty<DialogResult, string>(
                r =>
                {
                    if (ButtonNames.ContainsKey(r)) return ButtonNames[r];
                    return r.ToString();
                }, 
                (r, val) => ButtonNames[r] = val);
        }
    }

Later...

tableLayoutPanel2.Controls.Add(new Button 
    {
        Text = ButtonName[btn], DialogResult = btn, 
        Anchor = AnchorStyles.Right
    });

And the ButtonNames can be set from outside the class like:

msgbox.ButtonName[DialogResult.Cancel] = "Don't Do The Thing";

Solution 2:[2]

You'll need to create a new type, have Foo return an instance of that type, give that other type an indexer, and have that indexer do whatever you want it to do.

Solution 3:[3]

A class in C# cannot have named indexers but can have multiple indexers with different signature. A possible approach is using different indexer interfaces and let your "named indexer" just return the class itself casted to the proper "interface".

Your example would become:

class MyClass: MyClass.IFooIndexer, MyClass.IBarIndexer
{
    public interface IFooIndexer { object this[int index] { get; } }
    public interface IBarIndexer { object this[int index] { get; } }

    protected Dictionary<string, object> data = new Dictionary<string, object>();

    //implementing Interfaces
    object IFooIndexer.this[int index] { 
        get => data[$"foo{index}"]; 
    }
    object IBarIndexer.this[int index] {
        get => data[$"bar{index}"];
    }

    // these are your "named" indexers
    public IFooIndexer Foo => (IFooIndexer)this;
    public IBarIndexer Bar => (IBarIndexer)this;
    
    //add some data to test with
    public MyClass()
    {
        data.Add("foo15", "<foo 15 value>");
        data.Add("bar12", "<bar 12 value>");
    }
}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Community
Solution 2 Servy
Solution 3 Merilix2