Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Should it be possible to set a C# init-only property at run time?

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.

like image 282
Tony Stachnicki Avatar asked Apr 02 '21 09:04

Tony Stachnicki


2 Answers

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
like image 58
Marc Gravell Avatar answered Oct 19 '22 08:10

Marc Gravell


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)

like image 33
PMF Avatar answered Oct 19 '22 09:10

PMF