-
Notifications
You must be signed in to change notification settings - Fork 6.1k
Description
First of all, the called method can't directly assign to an in parameter. It can't directly assign to any field of an in parameter when that value is a struct type. In addition, you can't pass an in parameter to any method using the ref or out modifier. These rules apply to any field of an in parameter, provided the field is a struct type and the parameter is also a struct type. In fact, these rules apply for multiple layers of member access provided the types at all levels of member access are structs. The compiler enforces that struct types passed as in arguments and their struct members are read-only variables when used as arguments to other methods.
Statement “In fact, these rules apply for multiple layers of member access provided the types at all levels of member access are structs.” uses term member while it should be field. When method (like property or indexer getter) return value, it is copied. So readonly protection (CS8332) is enforced only on field access chains.
struct Outer { public Inner Inner { get; set; } }
struct Inner { public object Obj { get; set; } }
class Tests
{
void Test ( in Outer outer )
{
Inner inner = outer.Inner;
inner.Obj = default; // No compiler error – working with Inner copy.
// Error CS1612 Cannot modify the return value of 'Outer.Inner' because it is not a variable
outer.Inner.Obj = default;
}
}
Sample show that when value type is returned, it gets copied. Since it is not assigned locally, cannot be assigned to its member (CS1612).
╍╍ ╍╍╍╍ ╍╍╍╍ ╍╍╍╍ ╍╍╍╍ ╍╍
Since struct cannot return by-ref, AFAIK there is no option how get-access to struct-type member of struct could return without copy of that member value. That means that layer access protection applies only to fields.
struct RefStruct
{
int _int;
// Error CS8170 Struct members cannot return 'this' or other instance members by reference
public ref int Int => ref _int;
}Statement “The compiler enforces that struct types passed as in arguments and their struct members are read-only variables when used as arguments to other methods.” is rather unfathomable. Again it “suddenly” discusses members while fields were in question. Let assume fields. If so, this statement tells nothing untold. Otherwise it implies impossible – property, indexer, event, delegate, type, …, cannot be passed
I guess this was about to note defensive copy logic instead of recapitulating deep immutability characteristics of readonly value type reference.
This description better got reworded. It tries to explain something what is quite simple in a way that is not simple, or at least in not well-arranged way. Imagine that even if in modifier causes pass-by-ref, type kind is still treated as if was not pointer, i.e. it is reference or value. Then:
- For reference is not possible only to assign to
inparam. This satisfies the condition, reference (of reference type) at original storage is preserved, i.e. it is perfectly exhibited well-known shallow immutability behavior of readonly reference variables. - Value type follows same protection since otherwise original location would start pointing to another value and of course it is not possible to alter any field since this would alter the value – image if some DateTime like value was added 2 days internally. Similarly to 1. shallow immutability applies to reference type fields.
struct MyStruct
{
public object Object;
public int Int;
}
void Test (in MyStruct myStruct)
{
// Error CS8332 Cannot assign to a member of variable 'in MyStruct' because it is a readonly variable
myStruct.Int = default;
// Error CS8332 Cannot assign to a member of variable 'in MyStruct' because it is a readonly variable
myStruct.Object = default;
}
class N { public object obj; }
void Test ( in N n )
{
n.obj = default;
n = default; // Error CS8331 Cannot assign to variable 'in Container.N' because it is a readonly variable
}FMPOV it is better to introduce term shallow and deep immutability and rather replace behavior description with more separated information like table or separated list for in param of reference type and in param of value type.
Document Details
⚠ Do not edit this section. It is required for docs.microsoft.com ➟ GitHub issue linking.
- ID: ac8804c7-6acd-ab2a-4e14-0b99265969a5
- Version Independent ID: 183efcbd-a781-d303-f9cd-1c22da1676de
- Content: Write safe and efficient C# code
- Content Source: docs/csharp/write-safe-efficient-code.md
- Product: dotnet-csharp
- Technology: csharp-advanced-concepts
- GitHub Login: @BillWagner
- Microsoft Alias: wiwagn