Allow "modifying" the return value of a property getter with readonly setters #8364
-
At the moment, the language considers calling the setter of any property on a value type to be "modifying" the value, and thus disallows it when the value is returned by a method (including a property getter): struct S
{
public int Prop { get; set; }
}
static S SValue => default;
SValue.Prop = 10; // Cannot modify the return value of 'SValue' because it is not a variable There is a good rationale for this restriction, as any modification done to the fields of the value type is not observed, and thus the code is likely a mistake (even though this could be a warning in my opinion since the setter could do much more than setting a field). However, there are certain value types whose (settable) properties do not modify the state of the value type, such as public readonly struct ArraySegment<T>
{
readonly T[]? _array;
readonly int _offset, _count;
public T this[int index]
{
get => _array![_offset + index];
set => _array![_offset + index] = value;
}
} This means there is a certain lack of parity when accessing arrays through class MyClass
{
readonly byte[] storage = new byte[10];
public Span<byte> Storage => storage.AsSpan();
public ArraySegment<byte> StorageSegment => storage;
}
var obj = new MyClass();
obj.Storage[0] = 10; // fine
obj.StorageSegment[0] = 10; // error - modifying return value It seems this restriction has already been lifted once in regards to
In the example with |
Beta Was this translation helpful? Give feedback.
Replies: 7 comments 11 replies
-
Add readonly modifier on your setter. var c = new C();
c.GetS().X = 2; // No error.
struct S(int[] array)
{
public int X
{
get => array[0];
readonly set => array[0] = value;
}
}
class C
{
private readonly S _s = new([1]);
public S GetS() => _s;
} |
Beta Was this translation helpful? Give feedback.
-
I agree with the idea but not with the example. With the given |
Beta Was this translation helpful? Give feedback.
-
This is exactly what is needed in order to simulate named indexes without a class allocation (#471 (reply in thread)): If the wrapper struct (or even just the indexer's set accessor) is readonly, you're definitely not in need of a followup assignment back to the property: var c = new C();
// Proposal: no error because the indexer's set accessor is readonly.
c.SimulatedNamedIndexer[42] = new object();
class C
{
public WrapperStruct SimulatedNamedIndexer => new(this);
public readonly struct WrapperStruct(C c)
{
public object this[int index]
{
// Indexer accesses private state or calls private methods in 'c'
get => ...;
set => ...;
}
}
} |
Beta Was this translation helpful? Give feedback.
-
Similarly, this is blocked but it would not have to be blocked; the blocking could be done on a per-nested-member basis, allowing members marked as // ❌ CS1918 Members of property 'C.ArraySegmentProp' of type 'ArraySegment<object>' cannot be assigned with an object
// initializer because it is of a value type
_ = new C { ArraySegmentProp = { [42] = new object() } };
// ~~~~~~~~~~~~~~~~
class C
{
public ArraySegment<object> ArraySegmentProp { get; set; }
} Unsure of the usefulness of making this more granular, though. |
Beta Was this translation helpful? Give feedback.
-
This is true, the compiler does behave as you describe, but the v7 standard does not account for this. I opened dotnet/csharpstandard#1277 to fix it and potentially discover whether it is a compiler bug that the restriction was lifted for invocation expressions (e.g. methods), but not for other expressions (e.g. simple member access expressions, e.g. properties). |
Beta Was this translation helpful? Give feedback.
-
I believe this discussion is a duplicate of #2068. At the time, it covered methods as well as properties, but the properties discussion remains. |
Beta Was this translation helpful? Give feedback.
-
Proposal to lift this restriction: #9174 |
Beta Was this translation helpful? Give feedback.
Proposal to lift this restriction: #9174