7

Say you have a class like this

public class Foo
{
   public int Bar { get; set; } = 42;
}

If you try to pass the property as a ref parameter the compiler issues the error

CS0206 A property or indexer may not be passed as an out or ref parameter

This is understandable since in practice the property in the above example is compiled into get_Bar() and set_Bar() methods. But if you use an increment operator on the property, like

var foo = new Foo();
foo.Bar++;

it works as expected. To achieve this, the compiler needs to produce something like this pseudo code:

var foo = new Foo();
int tmp = foo.get_Bar();
tmp++;
foo.set_Bar(tmp);

So in theory the compiler could do a similar thing for ref like:

var foo = new Foo();
int tmp = foo.get_Bar();
DoSomething(ref tmp);
foo.set_Bar(tmp);

Is there a technical reason why the compiler doesn't do that or was this just a design decision of the C# team?

  • 2
    That is in fact what the VB.NET compiler does to make such code compile. It is the kind of language that was meant to be *friendly*, C# was meant to be *pure*. Where purity isn't very compatible with hiding a setter call that can be potentially expensive, generate an exception in the wrong place, might generate a hard-to-diagnose compile error or cause an aliasing problem. The ++ operator is however not very pure and skirts the rules in more than one way. You can increment a *byte* without a cast for example. They decided to make it *practical*. Choices. – Hans Passant Nov 27 '16 at 12:07
  • Possible duplicate: http://stackoverflow.com/questions/1402803/passing-properties-by-reference-in-c-sharp – Abion47 Nov 27 '16 at 12:42
  • @HansPassant I'd accept it if you could elaborate your comment into an answer – Bill Tür stands with Ukraine Nov 27 '16 at 17:46
  • @Abion47 I don't think that this is a duplicate as the question you linked doesn't address increment/decrement operators. – Bill Tür stands with Ukraine Nov 27 '16 at 17:48
  • 2
    I can't elaborate, I was never invited to a C# team design meeting. SO users demand facts instead of guesses, as well they should. Eric Lippert might show up, although he wasn't around either when this was decided. Your question just isn't answerable, it isn't that important that it get answered either so no major loss. – Hans Passant Nov 27 '16 at 18:16

1 Answers1

3

Like HansPassant said, this was a design decision that the C# team made when writing the C# specification, so you would have to ask one of them in order to get a proper answer.

If I was to hazard a guess, though, it would be that the amount of compiler magic to get passing a property with ref would cause enough unobvious operations to happen behind the scenes as to make the solution undesirable. For example, how incrementing/decrementing a property works currently is as you say: the program assigns the value of the property's backing field to a temporary variable, performs the operation, and reassigns the result to the property. It's a straightforward process that doesn't incorporate any difficult concepts.

To do the same behind the scenes magic to pass a property with ref, however, the process becomes a bit more involved. When a value type is passed by ref, the actual value getting passed through the parameter is a pointer to the value type variable. To do this for a property, though, you would have to do something akin to your second example. That would result in the address of the temporary variable, not the property itself, to be passed to the method. That kind of behavior could cause some unforeseen and difficult to understand consequences for someone trying to manipulate the ref parameter in certain ways.

So there's my guess, that the increment operator is simple to wrap because it only deals with values, whereas the ref keyword is more complicated because it has to worry about scopes and memory addresses as well.

EDIT: Another reason that occurred to me, for a field, any manipulations inside the called method would be reflected on the field itself. These manipulations could be seen by other threads and such that accessed that field during the method's execution (best practices about concurrent field accessibility aside).

For a parameter, however, any changes that happen inside the method wouldn't be visible until the method returned and the value copied back over. This would lead to inconsistent behavior between fields and properties, the cause of which wouldn't be readily apparent.

(Personally, I think this is a more probable reason to not support ref on properties.)

Abion47
  • 22,211
  • 4
  • 65
  • 88