openlang

Why can’t my interface be implemented by a property whose setter has restricted visibility in C#?

I'm attempting to give a class a protected property. An interface implemented by that class declares the same property too.

public interface IReading<T> {
    public T Adjustment { get; protected set; }
}

public class Reading : IReading<float> {
    public float Adjustment { get; protected set; } = 0.0f;
}

But the compiler reports this:

'Reading' does not implement interface member 'IReading.Adjustment.set'. 'Reading.Adjustment.set' is not public.

CS0277 (https://msdn.microsoft.com/query/roslyn.query?appId=roslyn&k=k(CS0277))

  • Taking the access modifier off the class property eliminates the error

  • Taking the access modifier off the interface property makes no difference

  • Taking the access modifier off both eliminates the error

I take this to mean C# won't allow me to express this arrangement. Still, I'd really like to understand why I can't require every implementation of the IReading interface to implement this property as a protected member.

I also looked at Why can't a non-public interface member be implemented implicitly? (https://stackoverflow.com/q/68592353) since the problem looked related, although I don't believe my implementation here is implicit.

The conversation continues 2

Follow the reasoning
  1. Permalink

    Unfortunately, an interface member marked protected doesn't behave the way you're expecting.

    That modifier is somewhat unusual on interface members, although the C# specification (the draft v8 text (https://github.com/dotnet/csharpstandard/blob/draft-v8/standard/basic-concepts.md#754-protected-access), at least) offers some explanation:

    Interface instance members whose accessibility is protected or private protected aren't accessible from its implementing classes or structs; only interfaces deriving from it may access them.

    The distinction matters for default implementations of interface members. Here's an example (I've omitted the generic parameter because it doesn't affect this):

    public interface IReading {
        public float Adjustment { get; protected set; }
    }
    
    public interface IExtendedReading : IReading {
        public float AdvanceAdjustment() => Adjustment++;
    }

    An explicit implementation in the concrete class lets you avoid exposing a publicly accessible setter, using either an auto-implemented property:

    public class Reading : IReading {
        float IReading.Adjustment { get; set; }
    }

    ... or implementing the accessors in full:

    public class Reading : IReading {
        private float adjustment;
        float IReading.Adjustment {
            get => adjustment;
            set => adjustment = value;
        }
    }

    The setter then isn't exposed publicly to other classes, though this still doesn't achieve the behavior you're seeking.

    An abstract class may be a better choice than an interface for this.

  2. Permalink

    ed_fern's explanation covers this well. I therefore won't repeat that discussion.

    My recommendation is to put a property with just a getter in the interface. That's the part exposed publicly regardless, including when the modifier is protected. The implementation can then supply a setter with whichever accessibility you need (private, private protected, protected or public (meaning plain set; without any modifier) or you could use init; rather than set;.

    Adding the required modifier to that property is another option. Doing so wouldn't break the interface contract.

    public interface IReading<T> {
        public T Adjustment { get; }
    }
    
    public class Reading : IReading<float> {
        public float Adjustment { get; protected set; } = 0.0f;
    }

Other sides of the same question

  1. Choosing enum entries randomly with GDScript in Godot 3.3 without listing each one?

    Here's the enum declaration I'm using in GDScript: I'd like to choose an enum variant randomly without listing every possible…

    Replies 2Latest activity gdscript
  2. Nim remainder syntax

    How is modulus expressed in Nim? cell % 7 == 0 gives undeclared identifier: '%' Neither a Google search nor looking through S…

    Replies 2Latest activity nim
  3. Returning results from an APL function

    How do I make my function return something once execution has finished? For instance, I have: ROUTINE N ? N ⍴ 12 //That gener…

    Replies 2Latest activity apl