Skip to content

readonly members on interfaces #3055

@IS4Code

Description

@IS4Code

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions