-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Description
I think readonly
members as introduced in C# 8.0 (#1710) should be usable on members of interfaces as well. The main reason is in this example:
public interface IVector<TVector> where TVector : struct, IVector<TVector>
{
readonly TVector Add(in TVector other);
}
TVector Add(in TVector a, in TVector b) where TVector : struct, IVector<TVector>
{
return a.Add(b); // shouldn't cause copying
}
If readonly
members on interfaces aren't available, there is no way to make a generic method for vector adding (or other operations) without copying. This is extremely useful once you start working with larger structures that are composed of multiple smaller types (like vectors and matrices). For example, if Vector
contains 4 float
values, Vector<Vector>
then contains 8 values, Vector<Vector<Vector>>
16 values; each containing two fields of the inner type:
public readonly struct Vector<TInner> where TInner : struct, IVector<TInner>
{
readonly TInner first;
readonly TInner second;
public readonly Vector<TInner> Add(in Vector<TInner> other)
{
return new Vector<TInner>(first.Add(other.first), second.Add(other.second)));
}
}
Without readonly
on IVector<TVector>.Add
, this code must copy first
and second
before calling Add
on the inner type, and there is no other way without abandoing readonly
and in
at all to prevent this, other than providing a separate object for operations with TVector Add(in TVector, in TVector)
method, at the cost of a virtual call. Marking the interface method readonly
means that the compiler knows the in
parameter will not be modified (at least by an implementation in C#).
readonly
should be allowed on interface members to indicate that the implementing method must honor readonly
as well. Then, if the implementing type is a struct, it must implement any readonly
method with another readonly
method. The other way around should be possible though, i.e. implementing a non-readonly
method by a readonly
one.
Relation to classes and #3885
Calling a readonly
method on structs already has a well-defined meaning: this
won't be modified from inside of the method. This is the meaning that is extended here to interfaces, but classes cannot change the value of this
, so in essence all methods are implicitly externally readonly
. A class can therefore freely implement an interface with readonly
methods without caring about readonly
anywhere in the implementation, because this
will never be modified. A readonly
variable of a reference type never requires creating a defensive copy when calling any of its methods, and is a non-issue if used in the example above.
This proposal is, for the most part, unrelated to #3885, but what needs to be resolved is whether readonly
on an interface should denote external readonlyness (this
is not observably modified) or internal readonlyness (can't write to fields and call non-readonly
methods). Value types share both meanings, because there is no indirection that could separate them. It seems that classes could only have internal readonlyness, unless a C++-style const
reference to an object is considered, but then it would warrant a new keyword anyway.
For that reason, even if readonly class
was added, the relation to interfaces would still be the same: a class implementing an interface with a readonly
method doesn't have to care about upholding readonly
on that particular method, because its can never be differentiated from the point of the interface.