-
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 ValueTuple members that can be made readonly readonly #90773
Conversation
You also have to update the reference assembly in https://github.com/dotnet/runtime/blob/64243bbf5e9ee53c0c4c5678f2cd8c7f1c9b4f6f/src/libraries/System.Runtime/ref/System.Runtime.cs#L7116-L7294. |
Thanks. |
Doesn't this break C# tuple contract? For example // Valid code
var tuple = (1, 2);
tuple.Item2 = 3; |
Only the 0-ary ValueTuple is marked as readonly as a whole type. The members of others are still writable. |
Tagging subscribers to this area: @dotnet/area-system-runtime Issue DetailsI applied readonly to all methods except for GetHashCode and ToString, which can't be made readonly (or can be, but it would make copies of the items) because they forward to methods on the individual items that could potentially modify them.
|
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.
Thanks for working on this.
How does GetHashCode()
and ToString()
create a defensive copy? sharplab.io shows identical asm.
Also, there have been other issues filed where this is being discussed. Just referencing them here.
- ValueTuple should use readonly methods #29403 (specific to ValueTuple)
- Determine Libraries strategy for using the readonly members annotation #1718 (more general approach)
- Methods "readonly" annotation #46675 (comment)
@dotnet/fxdc does applying @stephentoub @jkotas what is our current strategy for similar changes? I saw #46675 (comment)
And I believe that most of the methods marked as readonly in this PR (Equals, indexer, GetHashCode) will never mutate. But it's not very clear to me what we would gain from it. |
We've generally preferred to do so, yes, ideally in bulk.
The main consumer gain for us marking something Let's say you have: (long, long, long, long) value = ...;
Foo(ref value);
...
void Foo(ref (long, long, long, long) data)
{
data.GetHashCode();
} The compiler doesn't need to do anything special here around preventing mutation. But if instead the code was: (long, long, long, long) value = ...;
Foo(in value);
...
void Foo(in (long, long, long, long) data)
{
data.GetHashCode();
} The void Foo(ref (long, long, long, long) data)
{
(long, long, long, long) copy = data;
copy.GetHashCode();
} Whether that has any actual performance impact depends on a variety of factors, e.g. if the method being called is inlined, there's a good chance the JIT will eliminate the copy. |
{ | ||
return Comparer<T1>.Default.Compare(Item1, other.Item1); | ||
} | ||
|
||
int IStructuralComparable.CompareTo(object? other, IComparer comparer) | ||
readonly int IStructuralComparable.CompareTo(object? other, IComparer comparer) |
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.
Putting readonly
on explicit interface implementations isn't going to buy you anything. These can only be accessed via interface references and those don't have defensive copy semantics.
I'm actually a bit surprised we allow this syntax.
The other notable case is That being said, this is all generic code and we're just trading one copy for another copy here. That is, today, if you have If you instead mark In the case where all types are |
The below sharplab link gives a simplistic example of a struct where |
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.
does applying readonly to a type and/or method requires going through API review (removing it would cause a breaking change)?
We've generally preferred to do so, yes, ideally in bulk.
In such a case I am going to convert this PR to a draft and create a new API proposal based on the ref file changes.
Draft Pull Request was automatically closed for 30 days of inactivity. Please let us know if you'd like to reopen it. |
Before we close this, is this intended to still be one? From an API review standpoint, this affects public surface area and should normally go through API review but my care level is relatively low. Unless anyone on @dotnet/fxdc objects, I'm OK with letting these kind of changes be handled by PR review alone. |
Draft Pull Request was automatically closed for 30 days of inactivity. Please let us know if you'd like to reopen it. |
I applied readonly to all methods except for GetHashCode and ToString, which can't be made readonly (or can be, but it would make copies of the items) because they forward to methods on the individual items that could potentially modify them.