5

I tried writing some C# code to create an init-only property. I was surprised to find that the property could be changed at run-time. Have I misunderstood the idea of immutability? I am using Visual Studio Community 16.9.3.

Sample code.

namespace TonyStachnicki.Windows {
    using System.Management.Automation;

    [Cmdlet(VerbsCommon.New, "Person")]
    public class NewPerson : Cmdlet {
        protected override void ProcessRecord() {
            var _Person = new Person {
                Name = "Jane Dough"
            };
            WriteObject(_Person);
        }
    }
    public class Person {
        public string Name { get; init; }
    }

}

Run time example.

PS D:\Users\Tony> ($Person = New-Person)

Name
----
Jane Dough

PS D:\Users\Tony> $Person.Name = "John Smith"
PS D:\Users\Tony> $Person

Name
----
John Smith

PS D:\Users\Tony> $Person.Name = "Jane Jones"
PS D:\Users\Tony> $Person

Name
----
Jane Jones

PS D:\Users\Tony>

The program behaves the same with this Person class.

    public class Person {
        public string Name {
            get  { return m_Name; } 
            init { m_Name = value; }
        }
        private readonly string m_Name;
    }

In this case the readonly modifier is also ignored.
I think most people would be surprised at the effect of the init-only feature.

2 Answers2

3

Your C# code doesn't re-assign the value, so no C# rules were violated here. Whether powershell resects the rules is entirely up to powershell. Note:

  • the reflection API deliberately does not prevent access - this was so that serializers, ORMs etc worked without changes (and also so that the reflection API didn't need changes, which means it continues to work the same on older runtimes)
  • the more general IL access check depends on a "modreq" - but that is up to the relevant IL tools (usually a language compiler) to make decisions on; if a particular tool ignores "modreq", then: it ignores it
Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
  • Init-only properties are 'promoted' as making it easier to create an immutable property. But the property won't be immutable. – Tony Stachnicki Apr 02 '21 at 11:48
  • @Tony nothing is truly immutable; that concept simply doesn't exist. You show me just about anything in .NET: I can mutate it if I try hard enough; init-only only applies to things that care about either init-only, or modreq. In this case, it is PS that is breaking the rules, not C#; maybe PS is going via the reflection API, and never checked for modreqs. That's a discussion to raise with the PS folks. – Marc Gravell Apr 02 '21 at 14:39
1

From this source : "When the init keyword is used, it restricts a property to only being set by a Constructor or during Nested Object Creation." That means that

var _Person = new Person {
                Name = "Jane Dough"
            };

is legal (since it uses nested object creation). This is unlike the pre-C# 9.0 case, where you could define the property without a setter. This allows initialization only in the constructor, but not in a nested object creation.

Therefore, if you define your Person class as:

public class Person {
        public string Name { get; }
    }

your code would not compile (and you would need to provide a non-default constructor to your Person class to set Name to something)

PMF
  • 14,535
  • 3
  • 23
  • 49