openlang

Почему свойство с ограниченной доступностью setter не может реализовать мой интерфейс в C#?

Я пытаюсь добавить в класс защищённое свойство. То же свойство объявлено в интерфейсе, который этот класс реализует.

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

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

Однако компилятор выдаёт следующее:

'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))

  • Если убрать модификатор доступа у свойства класса, ошибка исчезает

  • Если убрать модификатор доступа у свойства интерфейса, ничего не меняется

  • Если убрать модификатор доступа с обеих сторон, ошибка исчезает

Я понимаю это так, что C# не позволяет выразить такую конструкцию. Но мне всё же очень интересно, почему нельзя потребовать, чтобы в каждой реализации интерфейса IReading это свойство реализовывалось как защищённый член.

Я также посмотрел вопрос о невозможности неявной реализации непубличного члена интерфейса (https://stackoverflow.com/q/68592353), поскольку проблема казалась похожей, хотя свою реализацию здесь я не считаю неявной.

Переведено с английскогоПоказать оригинал

Разговор продолжается 2

Следуя доводам
  1. Ссылка на ответ

    К сожалению, protected у члена интерфейса работает не так, как вы ожидаете.

    Для члена интерфейса это несколько необычный модификатор, хотя спецификация C# (по крайней мере, черновик v8 (https://github.com/dotnet/csharpstandard/blob/draft-v8/standard/basic-concepts.md#754-protected-access)) даёт некоторое объяснение:

    Члены экземпляра интерфейса с доступностью protected или private protected недоступны из реализующих его классов и структур; доступ к ним имеют только производные от него интерфейсы.

    Это различие важно для реализаций членов интерфейса по умолчанию. Например (параметр типа я убрал, поскольку здесь он ничего не меняет):

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

    Явная реализация в конкретном классе позволяет не делать setter общедоступным, например с автоматически реализуемым свойством:

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

    ... либо с полной реализацией аксессоров:

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

    Тогда setter не будет общедоступен другим классам, но нужного вам поведения это всё равно не обеспечивает.

    Здесь вместо интерфейса, возможно, лучше использовать абстрактный класс.

    Переведено с английскогоПоказать оригинал
  2. Ссылка на ответ

    ed_fern хорошо это объясняет. Поэтому повторять его разбор я не буду.

    Я бы рекомендовал оставить в интерфейсе свойство только с getter. Именно эта часть в любом случае видна публично, в том числе с модификатором protected. А реализация может добавить setter с нужной доступностью (private, private protected, protected или public (то есть просто set; без модификатора) либо использовать init; вместо set;.

    Ещё можно добавить этому свойству модификатор required. Контракт интерфейса это не нарушит.

    public interface IReading<T> {
        public T Adjustment { get; }
    }
    
    public class Reading : IReading<float> {
        public float Adjustment { get; protected set; } = 0.0f;
    }
    Переведено с английскогоПоказать оригинал

Другие стороны того же вопроса

  1. Как случайно выбирать варианты enum в GDScript на Godot 3.3, не перечисляя их?

    Вот объявление enum, которое я использую в GDScript: Хочу случайно выбирать вариант enum, не перечисляя все возможные вариант…

    Ответы 2Последняя активность gdscript
  2. Синтаксис остатка в Nim

    Как в Nim записать операцию взятия остатка? cell % 7 == 0 выдаёт undeclared identifier: '%' Ни поиск в Google, ни просмотр SO…

    Ответы 2Последняя активность nim
  3. Возврат результата из функции APL

    Как сделать так, чтобы моя функция возвращала значение после завершения выполнения? Например, у меня есть: ROUTINE N ? N ⍴ 12…

    Ответы 2Последняя активность apl