5

I was wondering whether it is possible to use C#'s ref return on (dictionary) indexers or properties which define a set and get accessor, e.g.:

readonly Dictionary<string, int> dictionary = ...;


ref int v = ref dictionary["foo"];
//          ^^^^^^^^^^^^^^^^^^^^^
// CS0206: A property or indexer may not be passed as an out or ref parameter
v = 42;

Is it possible to somehow provide ref functionality to properties or indexers (without using reflection)? If so, how?


I know, that the error message is clear in that sense - however, I was wondering which would be the optimal way to implement its semantics.
unknown6656
  • 2,765
  • 2
  • 36
  • 52
  • 1
    Since you effectively want to pass a setter in a delayed fashion, you could use an `Action` delegate, if your interfaces support it. – Jeroen Mostert May 25 '18 at 10:19
  • @TimSchmelter: I edited the question: I was wondering how the implementation of the _sematics_ could could be achieved – unknown6656 May 25 '18 at 10:19
  • 1
    If you want to do that specifically with built-in `Dictionary` - you have to wait a bit until this is implemented: https://github.com/dotnet/corefx/issues/20684 – Evk May 25 '18 at 10:26
  • ref is implemented as a pointer at runtime. Pointers require a memory location to point to. So a property or indexer can never qualify, they are methods and not memory locations. Not otherwise different from [C# v1.0 ref syntax](https://stackoverflow.com/a/4520101/17034) for a normal method parameter, you can't pass a property or indexer for the argument either. – Hans Passant May 25 '18 at 10:37
  • @HansPassant nowadays it's not quite true, since methods, including properties, can now return value by reference, and so certain properties (of type `ref Something`) can be passed to methods expecting `ref` argument of corresponding type. – Evk May 25 '18 at 10:47
  • Meh, it is true enough. The ref syntax has the same problem as async, it does not compose well. Triggers the turtles-all-the-way-down problem. They could not reasonably alter the framework classes to plunk ref flavors everywhere. – Hans Passant May 25 '18 at 11:12

2 Answers2

7

This requires the type implementing the indexer to provider ref-return in the indexer, so no: you can't use it with Dictionary<string, int>. But with something like this:

class MyRefDictionary<TKey, TValue>
{
    public ref TValue this[TKey key]
    {   // not shown; an implementation that allows ref access
        get => throw new NotImplementedException();
    }
}

You could indeed do:

ref var val = ref dictionary[key];

Note that arrays are a special case, as arrays have always allowed ref indexer access, i.e.

SomeMethod(ref arr[42]);

(the indexer access in arrays is implemented by the compiler, not the type)

Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
  • I was aware of achieving this using `ref return` readonly-properties, however, I thought that it might be possible to implement it the way arrays do it – unknown6656 May 25 '18 at 10:21
  • 1
    @Unknown6656 nope; that's just not possible; the compiler handles array indexers very differently to any other kind of indexer - there's specific IL instructions for array indexers - see `ldelem` / `ldelem_*` (which get the value) vs `ldelema` (which gets the `ref`) – Marc Gravell May 25 '18 at 10:23
  • 1
    @Unknown6656: Consider that an indexer is just syntactic sugar around methods that could literally implement the logic of getting and setting the value in an arbitrary complicated fashion. Now imagine stuffing that in a `ref int` somehow and you should appreciate the task the compiler would be faced with. – Jeroen Mostert May 25 '18 at 10:32
0

Answer is no, because Properties (or Indexers, which are special properties) are actually methods, and a ref to a method is a delegate.

So you can use Action<T> and your code becomes (not tested code):

readonly Dictionary<string, int> dictionary = ...;
Action<int> v = (x) => dictionary["foo"] = x;
v(42);
Martin Verjans
  • 4,675
  • 1
  • 21
  • 48
  • 2
    I don't think this represents the scenario being discussed in the question, which is specifically talking about getting a `ref` to the underlying value – Marc Gravell May 25 '18 at 10:27
  • @MarcGravell Agreed, I may have been interpreting the question too much. According to the posted code, I assumed the intent was to modify the underlying value. – Martin Verjans May 28 '18 at 12:34