views:

222

answers:

9

This is issue about LANGUAGE DESIGN.

Please do not answer to the question until you read entire post! Thank you.

With all helpers existing in C# (like lambdas, or automatic properties) it is very odd for me that I cannot pass property by a reference. Let's say I would like to do that:

foo(ref my_class.prop);

I get error so I write instead:

{
   var tmp = my_class.prop;
   foo(tmp);
   my_class.prop = tmp;
}

And now it works. But please notice two things:

  1. it is general template, I didn't put anywhere type, only "var", so it applies for all types and number of properties I have to pass

  2. I have to do it over and over again, with no benefit -- it is mechanical work

The existing problem actually kills such useful functions as Swap. Swap is normally 3 lines long, but since it takes 2 references, calling it takes 5 lines. Of course it is nonsense and I simply write "swap" by hand each time I would like to call it. But this shows C# prevents reusable code, bad.

THE QUESTION

So -- what bad could happen if compiler automatically create temporary variables (as I do by hand), call the function, and assign the values back to properties? Is this any danger in it? I don't see it so I am curious what do you think why the design of this issue looks like it looks now.

Cheers,

EDIT As 280Z28 gave great examples for beating idea of automatically wrapping ref for properties I still think wrapping properties with temporary variables would be useful. Maybe something like this:

Swap(inout my_class.prop1,inout my_class.prop2);

Otherwise no real Swap for C# :-(

+11  A: 

Properties are not variables, they are actually methods. Therefore, they cannot be passed as ref.

cornerback84
-0: The question is about design, not why this isn't possible in C#. But I guess the question was edited after you answered, so no -1.
SealedSun
It was edited, but answer should reflect whole post, not the title (summary). I write content of post for purpose.
macias
+1  A: 

Because a property is a method. It is a language construct responding to a pattern of encapsulating the setting and retrieval of a private field through a set of methods. It is functionally equivalent to this:

class Foo
{
    private int _bar;
    public int GetBar( ) { return _bar; }
    public void SetBar( ) { _bar = value; }
}
Ed Swangren
Did you actually read my WHOLE post?
macias
Heh, this snippet isn't exactly correct - it's more like `get__Bar()` and `set__bar(int value)`.
Dmitri Nesteruk
@Dmitri: No, its is a *functional* equivalent, not a reflection on what the compiler spits out.
Ed Swangren
+6  A: 

There are a lot of assumptions you can make about the meaning and behavior of a ref parameter. For example,

Case 1:

int x;
Interlocked.Increment(ref x);

If you could pass a property by ref to this method, the call would be the same but it would completely defeat the semantics of the method.

Case 2:

void WaitForCompletion(ref bool trigger)
{
    while (!trigger)
        Thread.Sleep(1000);
}

Summary: A by-ref parameter passes the address of a memory location to the function. An implementation creating a temporary variable in order to "pass a property by reference" would be semantically equivalent to passing by value, which is precisely the behavior that you're disallowing when you make the parameter a ref one.

280Z28
Thank you, I missed that. It seems that it should be some other keyword to wrapping property, something like:foo(in out my_class.prop);
macias
@macias: One possibility is to write it so you can go `my_class.prop = foo(my_class.prop);`
280Z28
It won't work for several arguments.
macias
A: 

Because, as Eric Lippert is fond of pointing out, every language feature must be understood, designed, specified, implemented, tested and documented. And it's obviously not a common scenario/pain point.

Damien_The_Unbeliever
This case is well understood such that it can't go on to the other points. This is not an example of limited developer resources.
280Z28
@280Z28 - You say that, and yet in other .Net languages (e.g. VB) you can certainly circumvent some of these expectations, such as passing a literal as a ref argument, and the compiler happily nodding and agreeing with it. So it *might* be an area that someone might want to explore. I was giving a more general answer since the OP was insisting that this was a language design question.
Damien_The_Unbeliever
But even then it's a comment, not really an answer.
280Z28
A: 

So -- what bad could happen if compiler automatically create temporary variables (as I do by hand), call the function, and assign the values back to properties? Is this any danger in it?

The danger is that the compiler is doing something you don't know. Making the code confusing because properties are methods, not variables.

PoweRoy
Given what the compiler does to implement `yield return`, I'm not convinced this specific argument is relevant to the problem at hand.
280Z28
+1  A: 

With a ref argument, changes to the underlying variable will be observed by the method, this won't happen in your case. In other words, it is not exactly the same.

Lasse V. Karlsen
I believe you are referring to what I put as Case 2 in my method which specifically has bad implications in a multithreaded environment.
280Z28
Doesn't even have to be multi-threaded. The property might be the the value of a temperature sensor, queried every time the property is accessed. Strictly single-threaded (unless you consider the real world a separate thread of course)
SealedSun
+1  A: 
var t = obj.prop;
foo(ref t);
obj.prop = t;

Here, side effects of getter and setter are only visible once each, regardless of how many times the "by-ref" parameter got assigned to.

Imagine a dynamically computed property. Its value might change at any time. With this construct, foo is not kept up to date even though the code suggests this ("I'm passing the property to the method")

SealedSun
The question the OP asked is quite literally "Why does the compiler not implement a property as a ref parameter by {insert your code snippet}."
280Z28
"what bad could happen if compiler automatically create temporary variables [..]? Is this any danger in it?" That's what I answered.
SealedSun
+4  A: 

Your proposal is called "copy in - copy out" reference semantics. Copy-in-copy-out semantics are subtly different from what we might call "ref to variable" semantics; different enough to be confusing and wrong in many situations. Others have already given you some examples; there are plenty more. For example:

void M() { F(ref this.p); }
void F(ref int x) { x = 123; B(); }
void B() { Console.WriteLine(this.p); }

If "this.p" is a property, with your proposal, this prints the old value of the property. If it is a field then it prints the new value.

Now imagine that you refactor a field to be a property. In the real language, that causes errors if you were passing a field by ref; the problem is brought to your attention. With your proposal, there is no error; instead, behaviour changes silently and subtly. That makes for bugs.

Consistency is important in C#, particularly in parts of the language that people find confusing, like reference semantics. I would want either references to always be copy-in-copy-out or never copy-in-copy-out. Doing it one way sometimes and another way other times seems like really bad design for C#, a language which values consistency over brevity.

Eric Lippert
"VB.Net removes this inconsistency and allows properties to be passes by reference. This is implemented under the hood by means of temporary variable."http://blogs.msdn.com/vbteam/archive/2010/01/26/the-many-cases-of-byref.aspx
adrianm
Great answer, and great comment -- thank you both. Now, I've heard that in time VB and C# should be more alike, i.e. features one has should go to the other :-)
macias
VB and C# have different design principles. VB designers value "just make it work the way the user thinks it should, even if that means adding unusual inconsistencies and special cases". C# designers value a clear, consistent programming model even if that means making you write longer code to express exactly what you mean. Both approaches are valid and reasonable; if you are the sort of programmer who agrees more with the former approach, consider switching to VB.
Eric Lippert
A: 

I'll provide just one simple example where it would cause confusion. Assume it was possible (as is in VB):

class Weird {
   public int Prop { get; set; }
}

static void Test(ref int x) {
   x = 42;
   throw new Exception();
}

static void Main() { 
   int v = 10;
   try {
      Test(ref v);
   } catch {}
   Console.WriteLine(v); // prints 42

   var c = new Weird();
   c.Prop = 10;
   try {
      Test(ref c.Prop);
   } catch {}
   Console.WriteLine(c.Prop); // prints 10!!!
}

Nice. Isn't it?

Mehrdad Afshari
I don't know how about VB, but adding finally in auto-generated helper is not a such big deal, so it would print 42 as well.
macias
@macias: it's not as easy as it seems. Assume you pass a couple properties and they throw exception in setters. The number of exceptions thrown can add up. You can't catch all of them and throw a single one.
Mehrdad Afshari