只读字段与抽象 getter-only 属性

Readonly field vs abstract getter-only property

与让继承者实现抽象 getter-only 属性 相比,拥有只读字段的优点和缺点是什么(这里以 C# 为例,但我想这不会真的很重要)。

以下是执行此操作的两种方法:

  1. 只读字段;继承者必须在构造函数中注入值

    interface IFace {
      public int Field { get; }
    }
    
    abstract class Base : IFace {
      private readonly int field;
    
      protected Base(int field) {
        this.field = field;
      }
    
      public int Field { get { return this.field; } }
    }
    
    class Impl {
      public Impl() : base(1) {
      }
    }
    
  2. 抽象getter-仅属性;继承人必须实施 属性

    interface IFace {
      public int Field { get; }
    }
    
    abstract class Base : IFace {
      // default constructor can be used
    
      public abstract int Field { get; }
    }
    
    class Impl {
      public override int Field { get { return 1; } }
    }
    

两种实现都公开了一个 public int Field getter-only 属性,它不会改变。

但是,我可以看到以下差异:

  1. field 的值绑定到每个 实例 并且没有什么可以阻止继承者允许在其构造函数中自己接收值(public Impl(int field) : base(field)).

    绑定到一个实例,每个实例都需要该字段的内存。这可能没什么大不了的,但这绝对是需要牢记的事情。

    传达的意图是:该值只能在构造函数中设置,以后不能更改(撇开反射)。

  2. Field 的(返回)值绑定到每个 类型 ,但没有什么可以阻止继承者 generating/calculating 该值每次调用 getter 时,每次都可能返回不同的值。 public overried int Field { get { return DateTime.UtcNow.Second; } }

    只需要内存 "in IL",因为该值(通常)不会存储在任何地方,但总是在返回之前进行计算(导致加载指令,仅此而已)。

    传达的意图应该是:值绑定到类型(并且不应在调用之间更改,但没有办法强制这样做,对吧?)。但意图是这样的:你需要提供这个 属性,我不在乎 你如何 实现它以及它的价值 returns.

有什么我遗漏的重要区别吗?是优先于另一个,还是需要根据具体情况来决定?

我想我正在寻找一个 construct/pattern/language 功能,它将只读(常量)值绑定到一个类型,但在实例级别公开该值。我知道我可以在每个继承类型中使用静态字段,但无法从公共基础(或接口)强制执行此操作。此外,当仅引用此类型的实例时,不能调用静态字段。想法?我很高兴收到不同编程语言的答案

您给出的模式 1 和模式 2 之间存在一个关键区别。

模式 1 不允许在构造 class 后 return 不同的值,因为基础 class 仅在构造函数中采用字段。

模式 2 允许子 classes 在不同时间 return 不同的值。基本上 - 如果子 class 决定覆盖,则 base class 不会强制执行任何操作。

因此 - 这实际上取决于您想要实现的目标和您的领域逻辑。

关于您试图实现的意图 - 在我看来 - 解决实现意图的方法之一是声明一个虚拟方法(类似于 base 中的 getReadOnlyField() )而不是只读 属性。然后 - 子 classes 可以自由覆盖虚拟方法 - 如果他们不覆盖 - 仍然会强制执行基本实现。

这个问题没有一个正确答案。将有多种方法来解决这个问题。这完全取决于您的要求。

我认为readonly-fields和abstract-getters是两个完全不同的概念。 readonly-field 是关于如何在 class 中使用该字段的。

一个抽象-getter都是关于class的接口。它没有对变量的使用方式施加任何限制,但它强制所有 class 继承者实现 getter 以满足接口。

实际问题应该是 public int Field 属性 的 public getter 在哪里;它应该在基础上还是在继承 class 上?答案(在我的选项中)取决于基数 class 是否必须知道字段 属性 的实际值。如果是这样,把它放在底座上,否则就强制所有子 classes 执行 属性 getter.

您的抽象定义了实施者必须遵守的契约。这超出了使用正确签名等实现方法的范围。违反它意味着违反 liskov 替换原则,即要求微妙或不那么微妙的错误。

如果有人觉得必须以某种方式强制执行合同,我能理解,但最终你无法强制遵守 LSP。您只能通过使用适当的文档并通常对哪些记录行为进行单元测试来尽可能清楚地说明意图。请记住,开发人员通常不会故意违反合同或 LSP。如果开发人员有恶意,那么所有的赌注都会被取消。

话虽这么说,但我想说的是,您所说的情况没有实际区别。是的,实现在句法和语义上是不同的,但其他 类 无论如何只会依赖于 IFace,对吗?说真的,如果已经有了抽象,就没有理由依赖具体的实现。因此,没有什么能阻止任何人为 IFace 创建一个新的实现并传递它。