-
Notifications
You must be signed in to change notification settings - Fork 4.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Make System.Guid readonly #1809
Conversation
Is the new code really better than marking just the non-mutating methods as Could we get a list of which instance methods mutate vs which do not? |
private readonly byte _h; // Do not rename (binary serialization) | ||
private readonly byte _i; // Do not rename (binary serialization) | ||
private readonly byte _j; // Do not rename (binary serialization) | ||
private readonly byte _k; // Do not rename (binary serialization) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is marking the fields as readonly going to break binary serialization?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just did a simple experiment of creating a struct with a non-readonly field, serialising it to a file, marking the field and the struct as readonly and deserialising it; It appears to work fine without exceptions. Though, I haven't really used binary serialisation so I could be wrong.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please verify the reverse as well since that's also supported (although, if ->readonly works, surely <-readonly does too)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The opposite seems to be true as well.
I'm not sure. None of the methods are documented to mutate the structure, and having every methods marked as readonly yet not having the type itself marked readonly because of its private implementation feels weird to me.
There actually is no instance methods that mutate. Only the ctors/static methods related with the creation of new Guid Instances actually mutate as part of the generation.
|
What would it take to refactor this cleanly, without introducing the MutableGuid type? |
Hmm, I guess the performance of parsing would regress without MutableGuid or it would need Unsafe hacks in more places. |
In this case, all the fields are private so existing user interop codes (in fact, any other code outside of the
For the parsing I suppose we could Introduce a bunch of local variables, continue with the same logic & call For the Guid.NewGuid maybe I should have turned it to something on the line of |
We have some other types, such as the However, marking the instance methods as I think it might be worth keeping the code simpler, especially since it is performance oriented, and just marking the various instance methods as readonly instead. |
In this case though, none of the fields are exposed, so it wouldn't be a breaking change to mark the whole type as
While it is true that marking all the instance methods would achieve the same effect, Marking the whole type readonly conveys that Guid is a immutable structure (which it really is). We could have easily went with an implementation that does not require mutation of the internal structure and mark it as |
I'll explore a bit more to see if there's alternative ways to approach this tomorrow (of course, while not compromising on the performance aspect). Just wondering - is there an "easy way" to obtain JIT output for those framework methods? |
Gave another thought about it... and realised, wouldn't we need to do the same thing even if we opt to mark individual members as readonly, as C# wouldn't still allow mutation of the fields in readonly methods? |
The MutableGuid type would go away, as it's only used in static methods. The ctor usages of Unsafe.AsRef would go away... GetHashCode and Equals might still need Unsafe.AsRef. |
Would it also make sense to have a unit test that iterates over all the public members of |
It looks like it'll be worth some extra discussion perhaps. Closing for now & Will open an issue. |
Related: #1718
This PR makes System.Guid as
readonly struct
. While none of the APIs mutate the instances, implementations of GUID parsing, copy and comparisons involve takingref
s of those fields and assigning new values, which is not allowed by C# with readonly fields.To work around these with minimal code changes,
Unsafe.AsRef
is used whenever we need to pass something asref
. These were really just for bitwise-comparison or for MemoryMarshal.TryWrite.I'm honestly surprised by the fact that MemoryMarshal.TryWrite takes
ref T
instead ofin T
as it does not mutate the passed reference.For the parsing implementation, rather large portions of it were mutating the guid; I ended up type-punning by creating a structurally identical struct then
Unsafe.As
ing it.Not sure if API review is required for this? All the public members should not mutate the instance already anyway (and would make this a mutable struct if any of them are, which is usually a bad idea)