user34537
user34537

Reputation:

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 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.

Upvotes: 2

Views: 287

Answers (4)

Ben Voigt
Ben Voigt

Reputation: 283803

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);
    }
}

Upvotes: 2

Zano
Zano

Reputation: 2761

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.

Upvotes: 0

Zach Johnson
Zach Johnson

Reputation: 24232

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.

Upvotes: 3

Kasper Holdum
Kasper Holdum

Reputation: 13373

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'

Upvotes: 2

Related Questions