Proposal: Allow setting readonly fields to null in Dispose #8724
Replies: 46 comments
-
The CLR forbids assigning |
Beta Was this translation helpful? Give feedback.
-
You could do some wacky things to protected readonly fields by inheriting and implementing |
Beta Was this translation helpful? Give feedback.
-
If you want to modify a field, then don't make it private readonly IDisposable disposable; as a code smell and worthy of a compiler/analyzer warning that Get rid of that |
Beta Was this translation helpful? Give feedback.
-
@DavidArno of course we can get rid of the readonly and make the code compile, problem is that it makes the code less clear, |
Beta Was this translation helpful? Give feedback.
-
If you want to prevent use of But is that better than checking a flag called |
Beta Was this translation helpful? Give feedback.
-
@DavidArno Thoroughly disagree that readonly references equate to immutable graphs. Whether or not you specify |
Beta Was this translation helpful? Give feedback.
-
@bondsbw agreed a flag is clearer. Most of the time I don't set things to null in the dispose method so |
Beta Was this translation helpful? Give feedback.
-
I usually don't bother nulling out the
I don't think I've yet encountered an object that visibly violates this. @JohanLarsson |
Beta Was this translation helpful? Give feedback.
-
@yaakov-h yeah it would be mutation but it would be restricted to ctor & dispose. |
Beta Was this translation helpful? Give feedback.
-
that's not |
Beta Was this translation helpful? Give feedback.
-
I'm confused by your assertion regarding the CLR and read-only fields. Does reflection use a weird loophole? That latter approach can be used to modify read-only fields: using static System.Console;
using static System.Reflection.BindingFlags;
namespace MutableReadonly
{
internal class Program
{
private readonly string _foo = "foo";
private static void Main()
{
var x = new Program();
WriteLine(x._foo); // prints foo
typeof(Program).GetField("_foo", Instance|NonPublic).SetValue(x, "bar");
WriteLine(x._foo); // prints bar
}
}
} To be clear, I'm not claiming that To my mind, the FxCop violation, CA2104: Do not declare read only mutable reference types hits the nail on the head. It's a shame in my view that this check was buried away in a rarely used tool, rather than being a compiler warning, but it is what it is. If I see private readonly SomeType _field;
...
_field.X = 1; I'd hesitate to call in an anti-pattern, but I definitely view it as poorly written code. You are of course entitled to disagree as that is just my opinion. |
Beta Was this translation helpful? Give feedback.
-
@DavidArno here is a bit of trivia for you: struct Foo
{
public readonly int Value;
public Foo(int value)
{
this.Value = value;
}
public void Update(int newValue)
{
this = new Foo(newValue);
}
} |
Beta Was this translation helpful? Give feedback.
-
😱😱😱😱😱 Why is that even possible? That's not an anti-pattern; that's a war crime! 😭 |
Beta Was this translation helpful? Give feedback.
-
@yaakov-h a pedant would say *twospexialmethods (ctor & dispose). Ctor is different though as the mutation is not observable from the outside. |
Beta Was this translation helpful? Give feedback.
-
@DavidArno I'm surprised that such a central tenet of C# is considered poor practice. It's still valuable to prevent mutation of the reference even when the target mutates. It's so common in OOP where the central tenet is that every object is a black box and it should be irrelevant whether it has internal state. |
Beta Was this translation helpful? Give feedback.
-
They come from math. It's an ordered, finite array of values. That's it. It makes no statement about mutability. Mutability is an advantage for C# because deconstruction can turn into a single |
Beta Was this translation helpful? Give feedback.
-
The core principle of tuples is to be an analog of an argument list, to the extent possible. |
Beta Was this translation helpful? Give feedback.
-
I didn't say it was expensive to pass them around. I said "But they're extremely expensive. Especially when code wants to actually change values. " Each immutable instance requires an allocation. Allocations are not free, and we've learned painfully over the past decade exactly how non-free they are. In a world where tuples are immutable classes, then every element change is also an allocation. Right now Roslyn itself can't use several C# features in certain areas because these costs are too high. I'm spending the majority of my engineering efforts on addressing this in the IDE because allocation cost, and hte impact to the user, is the single largest perf issue we have. |
Beta Was this translation helpful? Give feedback.
-
That's why i said it was a con. Because now that you have mutable classes, you can't safely share them. Someone might accidently mutate the tuple and break your copy. This is not an issue with the struct-form.
I did not say they are expensive ot pass around. I said they are expensive just as immutable classes are. See my previous post on this. Allocs and GC are extremely detrimental to performance in many domains. As people want things to run well on everything from Mobile to the Datacenter (where inefficiency translates to wasted money), we don't want to impose unnecessary costs on people. |
Beta Was this translation helpful? Give feedback.
-
This is the case when structs are quite large. The expected usage pattern in practice for tuples is usually <4 elements. In that case structs are fantastic for passing around. And if you are using a very large struct, then yes you can use ref. value-structs allow for fast perf for the overwhleming common case, as well as fast perf for the uncommon case. Importantly, they are safe to share around. |
Beta Was this translation helpful? Give feedback.
-
Nothing about that states that they should be immutable.
They really don't. Tuples predate FP significantly.
That was not your claim. Your claim was: "I just think it sad that the team have shifted the official guidance from "structs should be immutable unless really necessary" to "make 'em all mutable; it's efficient you know"." We have not shifted our guidance on structs. Our guidance on structs is that generally being mutable is probably not what you want. However, in some domains it's exactly what you want. And when that is the case, you should absolutely use them as they're great for this purpose. |
Beta Was this translation helpful? Give feedback.
-
My karma ran over your dogma. 🐕 🚗 Wait, what was this proposal about again? |
Beta Was this translation helpful? Give feedback.
-
I think you're mixing up concepts here. Take Roslyn as a good example. The Roslyn API embraces immutability heavily. Huge swaths of our API give you a surface area that is observably immutable and is safe to use a in massively threaded scenarios. However, internally, mutability is embraced heavily to be a means to present this immutable facade. It's the basis of incremental, lazy computation. Something which came heavily from the FP world. We achieve a scalable immutable API precisely by embracing mutability and using it effectively to provide something actually efficient. |
Beta Was this translation helpful? Give feedback.
-
It's maybe worth noting that the runtime will happily JIT code for non-ctor methods which modify Here's a proof of concept I made to test this a while back: ForceWriteReadonly. I can upload the full solution if someone wants to build this atrocity for some reason. (This also includes an example for calling a constructor as if it were a normal method, which somehow feels even more evil.) I imagine that producing verifiable assemblies is basically mandatory for Roslyn, so without CLR changes this proposal is a non-starter. IMO, CLR should not be aware of the IDisposable pattern, so I wouldn't think it should change either. My primary motivation for wanting a feature similar to this is that we null out As shown above, you could use an evil Fody weaver like I've shared to accomplish this feature if you really want it. However, you're better off using something like Fody.Janitor to add automatic throwing of TL;DR: You can do this if you really want to using evil IL manipulation, but you probably don't actually need this. |
Beta Was this translation helpful? Give feedback.
-
Couldn't code that alters readonly values in principle cause a segfault if the CLR for some reason would place the value in read-only memory? The CLR does already make assumptions that readonly values never change. Otherwise it's just as @DavidArno says: if you want to change it's value it shouldn't be readonly. What's the point of marking something readonly when it in practice isn't. |
Beta Was this translation helpful? Give feedback.
-
I don't picture a situation where CLR could/would do that. It'd require allocating a whole page of memory (usually 4 KB on Windows) for
I'm not really arguing in favor of this proposal, but to play devil's advocate: You might want to change |
Beta Was this translation helpful? Give feedback.
-
It's an implementation detail. The point is more that the CLR assumes that I wouldn't bet anything on all implementations of the CLR allowing code that writes to |
Beta Was this translation helpful? Give feedback.
-
Except that from the point of view of the runtime, the object's valid lifetime is from construction to finalization and IDisposable is irrelevant. |
Beta Was this translation helpful? Give feedback.
-
For sure. I presented my Fody weaver for the sake of trivia for the two of the two bigger implementations of CLR as that exist today. It's super evil and you should never do it, I did not mean to imply otherwise.
Correct, but |
Beta Was this translation helpful? Give feedback.
-
I'm also not in favor of moving that direction. #1451 (comment) |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
Beta Was this translation helpful? Give feedback.
All reactions