Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Are properties always immutable?

Tags:

c#

properties

I'm currently experiencing an issue when using an auto-generated property for a class member. Calling a method on the class should update the property, but it doesn't seem to work.

I'm updating some old C# code and VS2017 advises me few refactoring tips, for instance, private members with manually provided properties to auto-generated properties.

VS makes this easy with a couple of clicks, and everything compiles just fine. The problem is that code does not work as before.

Consider the following reduced code example:

using System;

public class Program    
{
    struct A
    {
        public A(int x) : this()
        {
            X = x;
        }

        public int X { get; private set; }

        public void Update(int y)
        {
            X += y;
        }
    }

    class B
    {
        private A _secondVar;

        public B()
        {
        }

        public A MyVar { get; set; }

        public A SecondVar
        {
            get { return _secondVar; }
            protected set { _secondVar = value; }
        }           

        public void Foo(int z)
        {
            MyVar.Update(z);
            _secondVar.Update(z);
        }       
    }


    public static void Main()
    {
        B b = new B();
        Console.WriteLine("BEFORE: b.MyVar: " + b.MyVar.X + ", b.SecondVar: " + b.SecondVar.X );
        b.Foo(23);
        Console.WriteLine("AFTER: b.MyVar: " + b.MyVar.X + ", b.SecondVar: " + b.SecondVar.X );
    }
}

The output is:

BEFORE: b.MyVar: 0, b.SecondVar: 0
AFTER: b.MyVar: 0, b.SecondVar: 23

I would expect that calling MyVar.Update(z) would update the property, and subsequent calls to MyVar.X should provide the updated value.

Is this expected behavior, or a bug in C#?

like image 575
1100101 Avatar asked Dec 23 '22 01:12

1100101


1 Answers

You're using mutable structs, which is strongly discouraged for precisely this reason.

Your code here:

public void Foo(int z)
{
    MyVar.Update(z);
    _secondVar.Update(z);
}

is equivalent to this:

public void Foo(int z)
{
    A tmp = MyVar; // Creates a copy
    tmp.Update(z);
    _secondVar.Update(z);
}

Your _secondVar.Update(z) code changes the value of _secondVar because you're calling it directly on a variable. That's not the case with your property - using a property getter is effectively calling a method that returns a value, which is a copy of the variable value.

If you were to use classes, you'd see the behaviour you expect, because then you wouldn't be trying to change the value of the underlying variable, but change the content within the object it refers to.

like image 135
Jon Skeet Avatar answered Jan 06 '23 11:01

Jon Skeet