dotnet / csharplang

The official repo for the design of the C# programming language
11.49k stars 1.02k forks source link

[Proposal]: Explicit Interface Implementation for property setter #4190

Open znakeeye opened 3 years ago

znakeeye commented 3 years ago

Explicit Interface Implementation for property setter

Summary

We can easily hide a method using Explicit Interface Implementation. It makes sense to allow a similar syntax for an interfaced property-setter. E.g.:

IDisposable.Dispose() {} // OK
public int MyProp { get; IMyInterface.MyProp.set } // Suggestion

Motivation

There are situations where you want to be able to set a property value, but for the general implementation you want to hide this capability. Similar to the IDisposable pattern where you might want only dispose-aware code to be able to call Dispose(). The naïve solution, is to define an interface like this and then implement it with a no-op setter. I'm including this since I have seen it in many projects (apparently not all developers are aware of the explicit interface implementation feature).

public interface IMyInterface
{
    int MyProp { get; set; }
}

public class NaiveClass : IMyInterface
{
    private int myProp;
    public int MyProp
    {
        get => myProp;
        set => throw new NotImplementedException();
    }
}

This naïve solution will allow access to the setter, but will cause a runtime failure if called. An improved version is shown below:

public class BetterClass : IMyInterface
{
    public int Property { get; private set; }
    int IMyInterface.Property
    {
        get => Property;
        set => Property = value;
    }
}

Too much typing! With my suggestion, you can define the property in a one-liner - just like those other nice property declarations!

Detailed design

Drawbacks

Alternatives

I can think of a few syntax alternatives.

public int MyProp { get; IMyInterface.MyProp.set }
// or
public int MyProp { get; IMyInterface.set }
// or
public int MyProp { get; explicit set; }

Unresolved questions

Design meetings

YairHalberstadt commented 3 years ago

Given how rare this need is, the current code seems like it's fine:

public class BetterClass : IMyInterface
{
    public int Property { get; private set; }
    int IMyInterface.Property
    {
        get => Property;
        set => Property = value;
    }
}
PathogenDavid commented 3 years ago

And if it's really common in your problem domain, a source generator could make quick work of the boilerplate.

msedi commented 3 years ago

It seems to me that you would need some sort of a read-only implementation (I cannot find the issue/discussion around this). I solved this by having read-only interfaces, regular interfaces and then some implementation (see below). But you are right. It's way too much typing for such a simple requirement.

    public interface IReadOnlyVolume
    {
        int SizeX { get; }
        int SizeY { get; }
        int SizeZ { get; }
    }

    public interface IVolume : IReadOnlyVolume
    {
        new int SizeX { get; set; }
        new int SizeY { get; set; }
        new int SizeZ { get; set; }
    }

    public class Volume : IVolume
    {
        public int SizeX {get;set;}
        public int SizeY {get;set;}
        public int SizeZ { get; set; }

        int IReadOnlyVolume.SizeX => SizeX;
        int IReadOnlyVolume.SizeY => SizeY;
        int IReadOnlyVolume.SizeZ => SizeZ;
    }
znakeeye commented 3 years ago

I solved this by having read-only interfaces, regular interfaces and then some implementation (see below).

Exactly. This is what I wanted to implement, but with code bloat.

msedi commented 3 years ago

@znakeeye: There is a discussion around deep immutability: #4015. Although I understand the problems that are discussed there I truly wish that something like this exists.