C# and immutability and readonly fields... a lie?

WPF-it picture WPF-it · Jul 17, 2011 · Viewed 9.6k times · Source

I have found that People claim that using all readonly fields in a class does not necessarily make that class's instance immutable because there are "ways" to change the readonly field values even after initialization (construction).

How? What ways?

So my question is when can we really have a "real" immutable object in C#, that I can safely use in threading?

Also do anonymous types create immutable objects? And some say LINQ uses immutable objecst internally. How exactly?

Answer

Eric Lippert picture Eric Lippert · Jul 17, 2011

You've asked like five questions in there. I'll answer the first one:

Having all readonly fields in a class does not necessarily make that class's instance immutable because there are "ways" to change the readonly field values even after construction. How?

Is it possible to change a readonly field after construction?

Yes, if you are sufficiently trusted to break the rules of read-only-ness.

How does that work?

Every bit of user memory in your process is mutable. Conventions like readonly fields might make certain bits appear to be immutable, but if you try hard enough, you can mutate them. For example, you can take an immutable object instance, obtain its address, and change the raw bits directly. Doing so might require a great deal of cleverness and knowledge of the internal implementation details of the memory manager, but somehow the memory manager manages to mutate that memory, so you can too if you try hard enough. You can also use "private reflection" to break various parts of the safety system if you are sufficiently trusted.

By definition, fully trusted code is allowed to break the rules of the safety system. That's what "fully trusted" means. If your fully trusted code chooses to use tools like private reflection or unsafe code to break the memory safety rules, fully trusted code is allowed to do that.

Please don't. Doing so is dangerous and confusing. The memory safety system is designed to make it easier to reason about the correctness of your code; deliberately violating its rules is a bad idea.

So, is "readonly" a lie? Well, suppose I told you that if everyone obeys the rules, everyone gets one slice of cake. Is the cake a lie? That claim is not the claim "you will get a slice of cake". That's the claim that if everyone obeys the rules, you'll get a slice of cake. If someone cheats and takes your slice, no cake for you.

Is a readonly field of a class readonly? Yes but only if everyone obeys the rules. So, readonly fields are not "a lie". The contract is, if everyone obeys the rules of the system then the field is observed to be readonly. If someone breaks the rules, then maybe it isn't. That doesn't make the statement "if everyone obeys the rules, the field is readonly" a lie!

A question you did not ask, but perhaps should have, is whether "readonly" on fields of a struct is a "lie" as well. See Does using public readonly fields for immutable structs work? for some thoughts on that question. Readonly fields on a struct are much more of a lie than readonly fields on a class.

As for the rest of your questions -- I think you'll get better results if you ask one question per question, rather than five questions per question.