Why can\'t you create a generic indexer in .NET?
the following code throws a compiler error:
public T this[string key]
{
get { /
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. */
}
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...
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.
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.
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
).
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];
}