4

I have classes

public class FooKeyedCollection : KeyedCollection<FooType,Foo>
{
}

public enum FooType
{
   FooType1,
   FooType2,
   FooType3
}

public Foo
{
   public FooType Type { get; set; }
   public string Value { get; set; }
}

When I add items to my FooKeyedCollection, I can index them by FooType, but when I attempt to index them int, the int is interpretted as the FooType enum. As a result, it leads to a confusing error, i.e.

public static void main()
{
   FooKeyedCollection fkc = new FooKeyedCollection();

   Foo myFoo = new Foo();
   myFoo.Type = FooType.FooType3;
   myFoo.Value = "someValue";

   foo.Add( myFoo );

   Foo myFooByType = foo[FooType.FooType3];
   Foo myFooByIndex = foo[0]; // <-- exception thrown here
}

The result of executing this code is an exception when attempting to retrieve the item by integer index. I hope to expose the FooKeyedCollection as part of a public API, and I want to protect the consumers of this API from this error. Is there any way that I can modify the FooKeyedCollection class to get proper behavior?

JadeMason
  • 1,181
  • 1
  • 14
  • 23

3 Answers3

3

You might consider using the "new" keyword here:

public new Foo this[int index]
{
   get
   {
      IList<Foo> self = this;
      return self[index];
   }
   set
   {
       (IList<Foo>)[index] = value;
   }
}

This will allow you to index by integer value. Using the enum explicitly will fall back to the KeyedCollection implementation.

Jeff Moser
  • 19,727
  • 6
  • 65
  • 85
3

Here's a helpful extension method to avoid these cases where the two indexers overlap - it also helps if the key is int:

static class CollectionUtils
{
    public static TValue GetByIndex<TKey,TValue>
        (this KeyedCollection<TKey, TValue> collection, int index)
    {
        return collection[index];
    }
}

then call fkc.GetByIndex(0) and it will work fine. Inside the generic method it can only resolve index to the int indexer.

Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
2

The problem is that 0 is implicitly convertible to FooType. If you use any other number, it'll be fine. If you use this code, it should work too:

int index = 0;
Foo myFooByIndex = foo[index];

EDIT: Unfortunately casting doesn't help here - the compiler still recognises it as a constant 0.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194