Why it is not possible to define generic indexers in .NET?

C#.NetGenerics

C# Problem Overview


Why can't you create a generic indexer in .NET?

the following code throws a compiler error:

public T this<T>[string key]
{
    get => /* Return generic type T. */
}

Does this mean you can't create a generic indexer for a generic member collection?

C# Solutions


Solution 1 - C#

Here's a place where this would be useful. Say you have a strongly-typed OptionKey<T> for declaring options.

public static class DefaultOptions
{
    public static OptionKey<bool> SomeBooleanOption { get; }
    public static OptionKey<int> SomeIntegerOption { get; }
}

Where options are exposed through the IOptions interface:

public interface IOptions
{
    /* since options have a default value that can be returned if nothing's
     * been set for the key, it'd be nice to use the property instead of the
     * pair of methods.
     */
    T this<T>[OptionKey<T> key]
    {
        get;
        set;
    }

    T GetOptionValue<T>(OptionKey<T> key);
    void SetOptionValue<T>(OptionKey<T> key, T value);
}

Code could then use the generic indexer as a nice strongly-typed options store:

void Foo()
{
    IOptions o = ...;
    o[DefaultOptions.SomeBooleanOption] = true;
    int integerValue = o[DefaultOptions.SomeIntegerOption];
}

Solution 2 - C#

I don't know why, but indexers are just syntactic sugar. Write a generic method instead and you'll get the same functionality. For example:

   public T GetItem<T>(string key)
   {
      /* Return generic type T. */
   }

Solution 3 - C#

Properties can't be generic in C#2.0/3.0 so therefore you can't have a generic indexer.

Solution 4 - C#

You can; just drop the <T> part from your declaration and it will work fine. i.e.

public T this[string key]
{
   get { /* Return generic type T. */ }
}

(Assuming your class is generic with a type parameter named T).

Solution 5 - C#

The only thing I can think of this can be used is something along these lines:

var settings = ConfigurationSection.AppSettings;
var connectionString = settings<string>["connectionString"];
var timeout = settings<int>["timeout"];

But this doesn't actually buy you anything. You've just replaced round parentheses (as in (int)settings["timeout"]) with angle brackets, but received no additional type safety as you can freely do

var timeout = settings<int>["connectionString"];

If you have something that's strongly but not statically typed, you might want to wait until C# 4.0 with its dynamic keyword.

Solution 6 - C#

I like the ability to have an indexer without handing out a direct reference to the "indexed" item. I wrote a simple "call back" Indexer class below ...

R = the returned type from the indexer P = the passed type into the indexer

All the indexer really does is pass the operations to the deployer and allow them to manage what actually occurs and gets returned.

public class GeneralIndexer<R,P>
    {
        // Delegates
        public delegate R gen_get(P parm);
        public delegate void gen_set(P parm, R value);
        public delegate P[] key_get();

        // Events
        public event gen_get GetEvent;
        public event gen_set SetEvent;
        public event key_get KeyRequest;

        public R this[P parm]
        {
            get { return GetEvent.Invoke(parm); }
            set { SetEvent.Invoke(parm, value); }
        }

        public P[] Keys
        {
            get
            {
                return KeyRequest.Invoke();
            }
        }

    }

To use it in a program or class:

private GeneralIndexer<TimeSpan, string> TimeIndex = new GeneralIndexer<TimeSpan,string>();

{
            TimeIndex.GetEvent += new GeneralIndexer<TimeSpan, string>.gen_get(TimeIndex_GetEvent);
            TimeIndex.SetEvent += new GeneralIndexer<TimeSpan, string>.gen_set(TimeIndex_SetEvent);
            TimeIndex.KeyRequest += new GeneralIndexer<TimeSpan, string>.key_get(TimeIndex_KeyRequest);

}

works like a champ especially if you want to monitor access to your list or do any special operations when something is accessed.

Solution 7 - C#

In recent C-sharp you can declare the return type as "dynamic". This is the same as using "object" except that the C# runtime will allow you to use it in code as if it was the type you think it is and then check at run-time to be sure you were right...

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionIgor ZelayaView Question on Stackoverflow
Solution 1 - C#Sam HarwellView Answer on Stackoverflow
Solution 2 - C#davogonesView Answer on Stackoverflow
Solution 3 - C#KevView Answer on Stackoverflow
Solution 4 - C#Greg BeechView Answer on Stackoverflow
Solution 5 - C#Anton GogolevView Answer on Stackoverflow
Solution 6 - C#Stuart PeacockView Answer on Stackoverflow
Solution 7 - C#Jeffrey KesselmanView Answer on Stackoverflow