6

I frequently find myself wanting to do something along these lines:

Form form = new Form();
form.ClientSize.Width = 500;

Of course the compiler will now complain that this code is not valid, since ClientSize is a property, and not a variable.

We can fix this by setting the ClientSize in its entirety:

form.ClientSize = new Size(500, ClientSize.Height);

Or, in general:

Size s = form.ClientSize;
s.Width = 500;
form.ClientSize = s; //only necessary if s is a value-type. (Right?)

But this is all looks unnecessary and obfuscated. Why can't the compiler do this for me? And of course, I'm asking about the general case, possibly involving even deeper levels of properties, not just the mundane example above

Basically, I'm asking why there is no syntactic sugar to translate the line form.ClientSize.Width = 500 into the above code. Is this simply a feature which hasn't yet been implemented, is it to avoid stacking of side effects from different getters and setters, to prevent confusion when one of the setters isn't defined, or is there a completely different reason why something like this doesn't exist?

JSQuareD
  • 4,641
  • 2
  • 18
  • 27
  • 7
    Why are mutable structs evil? http://stackoverflow.com/questions/441309/why-are-mutable-structs-evil – Andriy Tylychko Dec 17 '13 at 17:26
  • @AndyT I don't quite understand. Are you saying that in this particular case the standard library is evil (since Size is a struct, and most definitely mutable)? – JSQuareD Dec 17 '13 at 17:34
  • @JSQuareD The whole thing isn't evil. This particular type is, yes. – Servy Dec 17 '13 at 17:53
  • @JSQuareD: Some people would like to pretend that everything in the .NET universe is an Object, and regard as evil anything which doesn't behave like one. An exposed-field structure is simply a bunch of variables stuck together with duct tape, rather than an Object. If what one *wants* is a bunch of variables stuck together with duct tape, I would posit that it's better to use an exposed-field structure (and regard it as a bunch of variables stuck together with duct tape) than use a more inconvenient data type and pretend it's an object pretending to be a bunch of variables stuck together. – supercat Dec 28 '13 at 20:42
  • 1
    @JSQuareD: Incidentally, *any* structure type in .NET is just a bunch of fields stuck together, but structures can try to behave more like Objects. Mutable structures *can't* very well behave like Objects, and mutable structures which try to behave like objects are often problematic, but all that means is that *structures which are going to be mutable generally shouldn't pretend to be Objects*. If a structure isn't going to pretend to be an object, having exposed (mutable) public fields is a good way to make such lack of pretense clear. – supercat Dec 28 '13 at 20:47

3 Answers3

2

I believe that you have a fundamental misunderstanding of .NET here. You can set properties of properties all day for class types because you're modifying the data of a reference without changing the reference. Take this code for example which compiles and runs fine:

class Program
{
    public class Complex1
    {
        public Complex2 Complex2Property { get; set; }
    }

    public class Complex2
    {
        public int IntProperty { get; set; }
    }

    static void Main( string[] args )
    {
        // You must create instances of all properties to avoid a NullReferenceException
        // prior to accessing said properties
        var complex1 = new Complex1();
        complex1.Complex2Property = new Complex2();

        // Set property of property
        complex1.Complex2Property.IntProperty = 7;
    }
}

I assume your object is a struct or value type. The reason you can't do this for structs is that a struct is a value type - it gets copied around by value, not reference. So if I changed the above example to make Complex2 a struct, I could not do this line:

complex1.Complex2Property.IntProperty = 7;

Because the property is syntactic sugar for a get method which would return the struct by value which means a copy of the struct, not the same struct that the property holds. This means my change to that copy would not affect the original property's struct at all, accomplishing nothing except modifying a copy of my data that isn't the data in my property.

As for why the compiler doesn't do this for you? it definitely could, but it won't because you'd never want to actually do this. There's no value in modifying a copy of an object that you don't actually reassign to your property. This situation is a common error for developers who don't understand value vs reference types entirely (myself included!) and so the compiler chooses to warn you of your mistake.

Haney
  • 32,775
  • 8
  • 59
  • 68
  • 4
    His is a struct (`Size`) not a class, which is why his is illegal. – Nick Gotch Dec 17 '13 at 17:29
  • Great point @NickGotch - I didn't realize that. I'll update my answer. – Haney Dec 17 '13 at 17:32
  • Got it. But that still leaves the question of why the compiler doesn't handle such shenanigans for us, instead of making us have to jump through syntactic loops in order to modify this property. It's obvious that I do *want* to modify Complex2Property (or ClientSize), why should I care whether this means having to create a copy of it? – JSQuareD Dec 17 '13 at 17:38
  • You're not modifying it and that's the point. Value types are *immutable* aka read-only. When you change a property you're creating a *new* type, and that's why it prevents you from doing this... Said new type would not be the same reference that the first property holds. – Haney Dec 17 '13 at 17:47
  • Yes, I understand, thank you. This is why we have to explicitly make a new copy, or copy, modify, and copy back, as shown in my original question. But what I'm still curious about, is why the compiler can't do all this for us with some syntactic sugar? Is there any special reason for it? – JSQuareD Dec 17 '13 at 17:51
  • 3
    `"The reason you can't do this for structs is that a struct is an immutable type"` No, structs are not all immutable. They generally *should* be immutable, because [mutable structs are evil](http://stackoverflow.com/questions/441309/why-are-mutable-structs-evil), but in this case the OP has a *mutable* **class** with a property of a *mutable* struct. – Servy Dec 17 '13 at 17:55
  • Damned good point @Servy - you're all over me today. Updating answer. – Haney Dec 17 '13 at 18:10
  • Also the last paragraph is generally talking about "references" when you're talking about value types; it just doesn't make sense in that context. It's a value type; you're dealing with the actual object itself, not references to it. (At least in this example; you could be dealing with references to it if using pointers, or `ref/out` parameters.) – Servy Dec 17 '13 at 18:12
  • @Servy - fixed, you should like it a lot more now. :) – Haney Dec 17 '13 at 18:13
  • @DavidHaney It's better, but still flawed. Also, this doesn't answer the OP's actual question at all, it just explains why his code doesn't work, which isn't actually his question. – Servy Dec 17 '13 at 18:14
  • @Servy flawed in what way? – Haney Dec 17 '13 at 18:14
  • @DavidHaney See [this earlier comment of mine](http://stackoverflow.com/questions/20640717/why-can-we-not-set-properties-of-properties/20640796?noredirect=1#comment30898736_20640796). You're referring to references to value types in a way that just doesn't make sense. – Servy Dec 17 '13 at 18:15
  • 1
    K, it's now a correct explanation of the observed behavior, but it's still not actually answering the question that was asked, namely why the compiler doesn't apply the demonstrated transformation on the programmer's behalf. – Servy Dec 17 '13 at 18:23
2

Why can't the compiler do this for me?

It can. In fact, if you were programming in VB it would do exactly that. The C# compiler doesn't do this because it generally takes the philosophy of doing what you tell it to do; it is not a language that tries to guess at what you want to do and do that instead. If you tell it to do something silly, it'll just let you do something silly. Now this particular case is such a common source of bugs, and given these exact semantics is virtually certain to be a bug, so it does result in an error, because it's nice like that.

C# programmers learn to rely on the C# compiler never deciding to do something that you never told it to do, which can be a cause of confusion and problems when the compiler guess wrong about what you wanted to do.

Servy
  • 202,030
  • 26
  • 332
  • 449
1

For the compiler to allow myForm.ClientSize.Width = 500;, one of two things would be necessary: either the compiler would have to assume that the intended behavior is equivalent to:

var temp = myForm.ClientSize;
temp.Width = 500;
myForm.ClientSize = temp;

or else myForm would have to associate the name ClientSize with a method whose signature was:

void actupon_ClientSize<TParam>(ref Rectangle it, ref TParam param);

in which case the compiler could generate code similar to

myForm.actupon_ClientSize<int>((ref Rectangle r, ref int dummy)=>r.Width = 500, ref someDummyIntvar);

where someDummyIntVar would be an arbitrary value of type int [the second ref parameter would make it possible to pass parameters to the lambda without generating a closure]. If the Framework described a standard way for objects to properties to be exposed like that, it would make many types of programming safer and more convenient. Unfortunately, no such feature exists nor do I expect any future version of .NET to include it.

With regard to the first transformation, there are many cases where it would yield the desired effect, but also many where it would be unsafe. IMHO, there is no good reason why .NET shouldn't specify attributes which would indicate when various transformations are and are not safe, but they need for them has existed since Day One, and since the programmers responsible for .NET have consistently decided that they'd rather declare mutable structures to be "evil" than do anything that would make them be not evil, I doubt that will ever change either.

supercat
  • 77,689
  • 9
  • 166
  • 211