2

I would like to use different methods to implement the respective get and set case, like this:

public int this[int i]
{
    get { return i + 1; }
}

public string this[int i]
{
    set { }
}

This leads to error CS0111: Type 'Foo' already defines a member called 'this' with the same parameter types.

It seems this functionality cannot be achieved in the naive way. Is there a workaround?

I would like to use it like this:

class It {
    string SomeProperty;
}

class Bar {
    Action this[string key] {
        set {
            // ...
        }
    }
    string this[string key] {
        get {
            return new It ();
        }
    }
}

Bar ["key"] = () => {};
Bar ["key"].SomeProperty = 5;
mafu
  • 31,798
  • 42
  • 154
  • 247
  • Look it up here: http://stackoverflow.com/questions/424669/how-do-i-overload-the-operator-in-c-sharp – Maor Veitsman Oct 01 '15 at 08:51
  • 5
    No, you cannot do that. You cannot have multiple indexers that vary by return type only. – Lasse V. Karlsen Oct 01 '15 at 08:51
  • @MaorVeitsman That link does not address my question, though. – mafu Oct 01 '15 at 08:51
  • why not get set in one indexer – M.kazem Akhgary Oct 01 '15 at 08:51
  • One returns int, the other returns string. – Lasse V. Karlsen Oct 01 '15 at 08:52
  • @LasseV.Karlsen Yeah, I'm afraid so. Do you know of a workaround that simulates this behavior? – mafu Oct 01 '15 at 08:52
  • 1
    You cannot do this. Please [explain your original problem](http://meta.stackexchange.com/questions/66377/what-is-the-xy-problem). If you want to have a property that gets an int but is set with a string, you're out of luck. – CodeCaster Oct 01 '15 at 08:52
  • You can't have more than one 'this'. Also, why does a `set` needs to return a string? – kevintjuh93 Oct 01 '15 at 08:52
  • this problem is in general for get and set methods. properties have this problem too – M.kazem Akhgary Oct 01 '15 at 08:52
  • 2
    Wrap them inside sub-types which effectively gives them names. Why not use methods instead? – Lasse V. Karlsen Oct 01 '15 at 08:53
  • 2
    @kevintjuh93 You can have more than one `this[...]` but they need to vary by parameter types/count. – Lasse V. Karlsen Oct 01 '15 at 08:53
  • @LasseV.Karlsen Correct, sorry for not being clear enough. I meant to say like he does it. Having the amount of parameters for different returns. Still not clear why a `set` should return something... – kevintjuh93 Oct 01 '15 at 08:54
  • make return type `dynamic` but it reduces type safety. – M.kazem Akhgary Oct 01 '15 at 08:56
  • @M.kazemAkhgary I highly suggest to NOT do that – kevintjuh93 Oct 01 '15 at 08:57
  • I have added a usage example – mafu Oct 01 '15 at 08:59
  • @kevintjuh93 `set` needs a return type because it determines the accepted types of the `value` parameter. – mafu Oct 01 '15 at 09:00
  • @mafu I know, but it doesn't make sense in your context. Just make a `Set` method – kevintjuh93 Oct 01 '15 at 09:01
  • @LasseV.Karlsen I think your answer was useful, please don't delete it. – mafu Oct 01 '15 at 09:03
  • 1
    Your getter in the new example returns a string. Also, can you explain the usecase where you *set* the value to an action, but you *get* a completely different object? Can you give a concrete example of what you intend to use this for? – Lasse V. Karlsen Oct 01 '15 at 09:03
  • make one of the signatures to this. `public int this[int i, bool send_MeSome_Thing_So_ill_Be_Getter]` :-) – M.kazem Akhgary Oct 01 '15 at 09:07
  • @M.kazemAkhgary Splendid! :) – mafu Oct 01 '15 at 09:08
  • @LasseV.Karlsen `menu["do"] = () => {doStuff();}`, `menu["do"].help = "help: do does stuff"` - point is that the former will be used all the time while the latter is rare. However, *when* the latter is used, it would seem more logical than menu.Help["key"]. (Though it most certainly is an unusual thing to do!) – mafu Oct 01 '15 at 09:11
  • Sounds like you should just use a class that has both an action and a help property. `menu["do"] = new MenuItem(() => { doStuff(); }) { help = "" }`, `menu["do"].help = "help: do does stuff";` – Rawling Oct 01 '15 at 09:29
  • @mafu, technically you can do something like this with implicit operators like this `public static implicit operator It(Action a) { return new It(){ ... } }`, and use like: `menu["do"] = (Action)(() => {doStuff();})`, `menu["do"].help = "help: do does stuff"`, ofcourse if `It` class have property `help`. But simplest methinks like say @Rawling: `menu["do"] = new MenuItem(() => { doStuff(); }) { help = "" }` – Grundy Oct 01 '15 at 09:43
  • @Rawling Yeah, that's why I currently use. It is also less confusing to users, because the pattern I suggest in this question is really unusual, as pointed about by Lasse V. Karlsen. – mafu Oct 01 '15 at 09:58
  • @Grundy I thought about that, but figured that the implicit operator cannot get hold of the key in advance. However, now that you mention it, this could be mitigated by checking if the value in `set` has a key defined already, and if not, just merging it in. So yeah, that would be a solution to the question :) – mafu Oct 01 '15 at 10:01

1 Answers1

1

The indexer overload is a special property which accepts arguments. In VB.NET, due to the way VB handles collections, the property is given the name Item(...). If you look at the interface for IList<T> you'll notice it's called Item there too.

As a result, it has to follow the same rules as properties and method overloading. The return type of a method is not considered part of its calling signature, so overload resolution (how the compiler decides which version of a method to call) cannot distinguish a difference between your indexer implementations.

The intent of an indexer is to provide access to the values stored in a collection-like object. If you can get and set the value associated with a given key or index, the expectation is that you should get back the same value you set.

Your examples are trying to achieve a kind of type duality that isn't isn't the intent of an indexer and isn't achievable in the .NET type system. A type cannot simultaneously be an Action and a string. It works against fundamental object-oriented principals to try and make something be two things.

If you want to associate an action and a string, you should create a type that does just that:

public class NamedAction
{
    private readonly Action _action;
    public string Name { get; }

    public NamedAction(Action action, string name)
    {
        _action = action;
        Name = name;
    }

    public void Invoke()
    {
        _action.Invoke();
    }
}

Now you can have an indexer that gets and sets NamedAction instances and everything makes a lot more sense.

Paul Turner
  • 38,949
  • 15
  • 102
  • 166