openlang

为什么 setter 的访问权限受限时,C# 属性不能实现我的接口?

我想在类中定义一个 protected 属性。这个类实现的接口中也声明了同一个属性。

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 接口的每个实现都把这个属性实现为 protected 成员。

我还看了关于非公开接口成员为什么不能隐式实现的问题 (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. 在 Godot 3.3 的 GDScript 中怎样随机选择枚举项而不用逐项列出?

    这是我在 GDScript 中使用的枚举声明。 我想随机选择一个枚举变体,而不用列出所有可能的变体。这样以后增加变体时,就不必修改负责随机选择的代码。 我尝试过下面这种方法。 还有这一种。 两种都失败了。一个返回 Null,另一个报告 "Invali…

    回复 2最近活动 gdscript
  2. Nim 求余语法

    Nim 中怎样表示求余运算? cell % 7 == 0 会给出 undeclared identifier: '%' 这个错误 Google 搜索和查看 SO 都没找到答案。

    回复 2最近活动 nim
  3. 从 APL 函数返回结果

    怎样才能让函数在执行结束后返回一个值? 例如我有这样的代码。 ROUTINE N ? N ⍴ 12 //它会生成 N 个从 1 到 12 的随机值 (N 是函数的参数)。 我只是想知道怎样拿到函数的结果,然后比如把这个结果传给另一个函数。 谢谢大家的…

    回复 2最近活动 apl