views:

177

answers:

4

From http://csharpindepth.com/Articles/Chapter8/PropertiesMatter.aspx

using System;

struct MutableStruct
{
    public int Value { get; set; }

    public void SetValue(int newValue)
    {
        Value = newValue;
    }
}

class MutableStructHolder
{
    public MutableStruct Field;
    public MutableStruct Property { get; set; }
}

class Test
{    
    static void Main(string[] args)
    {
        MutableStructHolder holder = new MutableStructHolder();
        // Affects the value of holder.Field
        holder.Field.SetValue(10);
        // Retrieves holder.Property as a copy and changes the copy
        holder.Property.SetValue(10);

        Console.WriteLine(holder.Field.Value);
        Console.WriteLine(holder.Property.Value);
    }
} 

1) Why is a copy (of Property?) being made?
2) When changing the code to holder.Field.Value and holder.Property.Value = 10, I get the error below. That just blew my mind.

Cannot modify the return value of 'MutableStructHolder.Property' because it is not a variable

Why would I ever not be allowed to assign a value inside of a property!?! Both properties are get/set!

And finally WHY would you EVER want behavior mentioned in 1 and 2? (It never came up for me, I always used get only properties).

Please explain well, I can't imagine ever wanting the 2nd much less then the first. It is just so weird to me.

+2  A: 

Question 1: You should read up on structs and classes since the workings of these explains all the "symptons" you're describing. Structs aren't passed around by reference, but actually by making copies of the objects being passed around - copies being the keyword. So when property returns a struct it's actually a copy and not the "real" object you're changing the value of.

As for question 2: The C# compiler realises that setting a variable directly on an struct-object retrieved through a property is meaningless and thus provide you with error telling you so. Whether the object retrieved through the property has a setter or not does not change this behaviour and you will recieve the error either way.

Instead of doing a writeup on classes vs structs I suggest you read Lasse Karlsen's answer to the question on 'The difference between structs and classes'

Qua
+3  A: 

1) A copy of Property is being made because it is a struct, and a struct is a value type (unlike a class) and is copied by value, rather than reference.

2) The "cannot modify return value" error you see stems from the 'copy by value' nature of structs. If you modified the struct (setting Value on the MutableStruct through Property on MutableStructHolder), your changes would not be reflected on it because you would be modifying a copy of the struct, not the struct held in the property. Since you would never want this behavior, the C# compiler prevents you from doing that.

Now, if MutableStruct were a class, there would be absolutely no problem in modifying it through a property (and the C# compiler allows you to do that), since you would be working with the actual instance and not a copy.

Zach Johnson
A: 

Structs are passed by value, so all you ever have is copies. Also:

public int Value { get; set; }

is shorthand for

private int _Value;
public int Value { 
   get { return _Value; }  
   set { _Value = value; }
}

As you can see, there's a lot of copying going on, so the complaints are expected. If you instead use class, which is a reference type, you won't have this problem.

Zano
+2  A: 

To answer #1: Consider how it would look without an auto-generated property. You'd have Property { get { return xxx; } } That's where the copy is made. What looks like referring to a property is really calling a getter method. Would you expect an in-place edit if you had:

private MutableStruct v;
public MutableStruct f()
{
  return v;
}

f() = new MutableStruct();

Of course not, we all depend on return to make a copy of the struct so that the private variable is protected against outside modification. So that's why it works the way it does.

Now, if you're thinking that the property setter should have automatically been called with the resulting value, please let me point out that it's extremely difficult impossible to know at compile-time if a call to one of the struct's methods makes a change (consider if the struct is defined in a different assembly). And writing the value back isn't just inefficient-but-harmless, it might break correctness. Consider a multithreading scenario, where threads that were only reading from the property under protection of a ReaderWriterLock suddenly are writing back to it. All sorts of pain and misery would occur. So the C# compiler automatically setting the property to the result of your computation just isn't an option.

EDIT: You were wondering why setting a field on the struct generates an error message, but calling a method to do the same thing doesn't. Well, the method might have useful side effects, so you still need to be able to call it on a temporary copy of the struct. Consider the following variation:

struct Mutable
{
    public static int Sum = 0;

    public int x;
    public Mutable(int x) { this.x = x; }
    public void Total() { Sum += x; }
}

class Container
{
    public Mutable Field;
    public Mutable Property { get; set; }
}

class Program
{
    static void Main(string[] args)
    {
        Container c = new Container();
        c.Field = new Mutable(1);
        c.Property = new Mutable(2);
        c.Field.Total();
        c.Property.Total();
        Console.Out.WriteLine(Mutable.Sum);
    }
}
Ben Voigt
I completely forgot it would call 'get' to access the value property.
acidzombie24
@acidzombie: I can't understand this answer at all. The actual answer to #1, as correctly stated by other answers, is that MutableStruct is a value-type (struct).
BlueRaja - Danny Pflughoeft
@BlueRaja: The other explained structs are pass by value. This answer is saying why the struct is being passed (with get) and that i am not accessing Value directly (only set, no get) as i thought i was doing
acidzombie24
@BlueRaja: The question was why the property wasn't updated when it has an accessible setter. None of the other answers made any attempt to address why the compiler couldn't just use the setter to store the updated value back into the property.
Ben Voigt
The reason you couldnt reproduce the error msg (i seen you mention that in the edit history) is because i am using VS2010(rc1). You didnt need a code example, i only needed to be reminded there is a get before the set! :/
acidzombie24
@acidzombie24: I was testing your original code snippet, not the one with the field assignment. The error message is not generated for a method call for the reason I mentioned (side effects).
Ben Voigt