Why do properties behave this way?
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 created?
2) When changing the code to holder.Field.Value
and holder.Property.Value = 10
, I get the error below. It just blew my mind.
Cannot change the return value of 'MutableStructHolder.Property' because it is not a variable
Why was I not allowed to assign a value inside a property!?! Both properties: get
/ set
!
And finally, WHY would you like the behavior mentioned in 1 and 2? (It never crossed my mind, I've always only used properties).
Please explain, okay, I can't imagine I ever wanted the second to be much smaller than the first. This is so strange to me.
Answer # 1: Consider what it would look like without the auto-generated property. You will Property { get { return xxx; } }
have Where the copy is made. What looks like a property reference actually calls the getter method. You would expect in-place editing if you had:
private MutableStruct v;
public MutableStruct f()
{
return v;
}
f() = new MutableStruct();
Of course not, we all depend on returning to make a copy of the structure so that the private variable is protected from external modification. So why does it work the way it does.
Now if you think that the property setter should have been called automatically with the resulting value, please let me point out that it is extremely difficult to find out 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 is not just inefficient, but harmless, it can violate correctness. Consider a multithreading scenario where threads that have only read from a property protected by ReaderWriterLock suddenly return to it. All kinds of pain and suffering will appear. So a C # compiler automatically setting a property for the result of your computation is not an option.
EDIT: You wondered why setting a field in a struct generates an error message, but calling a method to do the same does not. Well, the method can have useful side effects, so you will still need to call it on a temporary copy of the structure. 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);
}
}
source to share
1) A copy is performed Property
since it is struct
, and struct
is a value type (as opposed to class
) and copied by value, not by reference.
2) The "cannot change return value" error you see from the nature of the "copy by value" structure. If you change the structure (setting Value
on MutableStruct
through Property
to MutableStructHolder
), your changes will not reflect on it, because you will be changing a copy of the structure, not the structure contained in the property. Since you will never want this behavior, the C # compiler will not let you do this.
Now, if there MutableStruct
was class
, there would be no problem modifying it through a property (and the C # compiler allows you to do this), since you will be working with an actual instance, not a copy.
source to share
Question 1: . You should read about structures and classes as these papers explain all the "symptoms" that you describe. Structures are not passed by reference, but in fact by copying object instances - the keyword is a copy. So when a property returns a structure, it is actually a copy and not the "real" object that you are changing.
Regarding question 2: The C # compiler understands that setting a variable directly on a structured object obtained via a property doesn't make sense and thus gives you an error. Whether the object obtained via a property is a setter or not does not change this behavior and you will get an error anyway.
Instead of making notes on classes vs structs, I suggest you read Lasse Karlsen's answer to the question " Difference between structs and classes
source to share
Structures are passed by value, so all you have are 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 are a lot of copies going on, so complaints are expected. If you use class
which is a reference type, you won't have this problem.
source to share