Search code examples
c#structimmutabilitysetter

Change the value of a property of a struct in C#


I was reading a book and found out that structs are actually immutable objects. But they have getters and setters. I was wondering if a property of structs can be changed after it has been created.

public struct Test 
{
    public string str {get; set; }
    public int int1 {get; set; }
}

Can the values of 'str' and 'int1' be changed once they have been assigned a value?


Solution

  • Structs can be either mutable or immutable, but they should be immutable according to many people.

    Your example is a mutable struct.

    Example of use:

    var t = new Test();
    // t.str is null, and t.int1 is 0
    
    t.str = "changed!"; // OK
    
    var t2 = t;
    t2.int1 = 42;
    // t.int1 is still 0
    
    var li = new List<Test> { t, t2, };
    t.int1 = 666;  // OK, but copy in li is unaffected
    
    li[0].int1 = 911;  // compile-time error, not a variable
    
    var t3 = t2;
    bool checkA = (t3 == t2);  // compile-time error, you did not overload operator ==
    bool checkB = t3.Equals(t2);  // OK, true, ValueType class overrides Equals for you
    bool checkC = t2.Equals(t);  // OK, false
    bool checkD = object.ReferenceEquals(t, t);  // false, two distinct boxes
                                                 // same as (object)t==(object)t
    

    By request, here is one way to make that struct immutable:

    public struct Test 
    {
        public string str { get; private set; }
        public int int1 { get; private set; }
    
        public Test(string str, int int1) : this()
        {
            this.str = str;
            this.int1 = int1;
        }
    }
    // if you introduce methods (other than constructors) that use the private setters,
    // the struct will no longer be immutable
    

    and here is another one:

    public struct Test 
    {
        readonly string m_str;
        readonly int m_int1;
    
        public string str { get { return m_str; } }
        public int int1 { get { return m_int1; } }
    
        public Test(string str, int int1)
        {
            m_str = str;
            m_int1 = int1;
        }
    }
    

    Update:

    Starting from 2015 (C# 6.0), you can write the latter as:

    public struct Test 
    {
        public string str { get; }
        public int int1 { get; }
    
        public Test(string str, int int1)
        {
            this.str = str;
            this.int1 = int1;
        }
    }
    

    In later versions there are many more possibilities. I shall only mention that you can write (since 2021 (C# 10)):

    public readonly record struct Test(string str, int int1) 
    {
    }
    

    The word record now means that the type automatically overrides or overloads stuff like Equals, GetHashCode, operator ==, ToString, Deconstruct, etc. And the constructor is now a primary constructor.