0

In the following program, think of A is a class provided through dlls and so there is no way to change it. Class B needs to be derived from A. It has to implement the abstract class's indexer. But the indexer has only a get section and I also need a set section which is not allowed for the implementation. So I tried to add an indexer with long parameter type to complement the the overridden indexer. But now, int a = b[10]has an error in the following program. What is wrong and how can I remove this error?

abstract class A
{
    public abstract int this[int i]  { get; }
}
class B : A
{
    public int a;
    public override int this[int i]
    {
        get => i;
    }
    public int this[long i]
    {
        set => a = value;
    }
}

class Program
{
    static void Main()
    {
        B b = new B();
        int a = b[(int)10];
    }
}

Removing long indexer removes the error! Why does it hide the int indexer completely?

Minimus Heximus
  • 2,683
  • 3
  • 25
  • 50
  • Why do you need a separate indexers for getter and setter? – Pavel Anikhouski Nov 16 '20 at 12:02
  • @PavelAnikhouski CS0154 The property or indexer 'B.this[long]' cannot be used in this context because it lacks the get accessor – Minimus Heximus Nov 16 '20 at 12:03
  • 1
    Unfortunately indexer support in C# kinda sucks. You simply can't do this. You can always provide a new `Set...` method or an explicitly named property for aliasing the `this`, but you cannot make `b[..] = ...` compile. There are [rather complicated workarounds](https://stackoverflow.com/q/22210971/4137916) for regular properties, but those won't work for indexers (without losing the indexer syntax, at least). If `A` were an interface it would be a completely different matter, of course, as then adding a setter would work out of the box. – Jeroen Mostert Nov 16 '20 at 14:29
  • @JeroenMostert My problem is that the `long` indexer has nothing to do with the `int` indexer but completely hides it. – Minimus Heximus Nov 16 '20 at 17:49
  • 1
    Interestingly, the error is also resolved when B isn't derived from A and this[int i] is defined without override. – Felix Lechenbauer Nov 16 '20 at 18:32
  • 1
    Those are the rules for overload resolution. If more than one method applies, preference is given to the most derived one, *before* preference is given to better matches of the argument type. Indexers do not behave differently with respect to methods here; you'll get the same behavior if you define `f(int i)` in `A` and `f(long i)` in `B`. The reasoning here is that adding a method to a base class should not suddenly result in a new method being selected for existing code. It avoids one type of brittle base class problem, at the expense of things like this. – Jeroen Mostert Nov 16 '20 at 18:43
  • 1
    Unfortunately whether `get` or `set` is being invoked doesn't change which indexer is selected either; resolving the indexer happens before that is applied. This is why you will always get the `long` indexer as long as you invoke it with something that could match a `long` argument, regardless of what's declared in `A`. As Willy Wonka might say, [it's all there, black and white, clear as crystal](https://learn.microsoft.com/dotnet/csharp/language-reference/language-specification/expressions#indexer-access), but it means you lose. – Jeroen Mostert Nov 16 '20 at 18:49

0 Answers0