tags:

views:

640

answers:

6

Does anyone knows why you can't 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?

+7  A: 

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).

Greg Beech
That will not work unless you define <T> at class level.
Igor Zelaya
@Igor - that's why I said "Assuming your class is generic with a type parameter named T". The question did not indicate that the type parameter should be different from the container (and in any case this would be a very unusual design!).
Greg Beech
@Greg...how unusual can it be? Take as an example the Field<> generic method in DataRow. What they did is unboxing through a generic method since it is impossible through a generic indexer.
Igor Zelaya
@Igor - it's the implication that's the issue. An indexer implies direct access to the actual object at that index, not a converted form of that object, whereas a method has no such implication. And just think of the syntax: someObject<int>["abc"] isn't very pretty.
Greg Beech
+3  A: 

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. */
   }
davogones
+7  A: 

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

HTH
Kev

Kev
Do you have a link to anything on that front? I can't see it in the "new features" document.
Jon Skeet
Jon you're quite correct, I'll retract that.
Kev
mmmm...I think I found the link you are looking for.http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=352188What It seems incredible to me is that Microsoft says it is not a useful feature but have already implemented some workaround extenders(i.e.DataRowExtender)
Igor Zelaya
@Igor - yep, I just ran across that right now.
Kev
+1  A: 

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 parens (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.

Anton Gogolev
Well, actually the settings<int> syntax is preferable. C-style casts often forces you to include additional parenthesis.
John Leidegren
@John I agree. I think that is nicer than creating method accessors like Filed<> and SetFiled<> in DataRow Class for example.
Igor Zelaya
+2  A: 

Here's a good explanation: http://www.boyet.com/Articles/GenericProperties.html

CodeMangler
+1  A: 

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 IOptionss
{
    /* 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];
}
280Z28