只读字段与仅抽象的getter属性

时间:2015-04-23 06:15:19

标签: c# language-agnostic

与继承者相比,拥有readonly字段有什么优点和缺点实现了一个抽象的getter-only属性(在这里使用C#作为例子,但我想这并不重要)。

以下是两种方法:

  1. readonly field;继承者必须在构造函数中注入值

    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-only属性;继承者必须实现属性

    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; } }
    }
    
  3. 这两个实现都公开了一个不会改变的公共int Field getter-only属性。

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

    1. field的值绑定到每个实例,并且没有什么可以阻止继承者允许在其构造函数本身中接收值({{1} })。

      绑定到实例时,每个单个实例都需要该字段的内存。这可能不是什么大问题,但它绝对值得记住。

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

    2. public Impl(int field) : base(field)的(返回)值绑定到每个类型,但是每次getter都不会阻止继承者生成/计算值调用,每次都可能返回不同的值。 Field

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

      传达的意图应该是:值与类型绑定(并且不应该在调用之间发生变化,但是没有办法强迫它,对吧?)。但更确切地说,意图是:您需要提供此属性,我不在乎 如何实现它以及它返回的值。

    3. 我有什么重要的差异吗?是一个优先于另一个,还是需要根据具体情况决定?

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

3 个答案:

答案 0 :(得分:2)

我相信 readonly -fields和 abstract -getters是两个完全不同的概念。 readonly -field是关于如何在定义的类中使用该字段的全部内容。

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

实际问题应该是找到public int Field属性的公共getter的位置;它应该在基础上还是在继承类上?答案(在我的选项中)取决于基类是否必须知道Field属性的实际值。如果是这样,请将其放在基础上,否则只需强制所有子类实现属性getter。

答案 1 :(得分:1)

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

一旦构造了类,模式1不允许返回不同的值,因为基类仅在构造函数中使用字段。

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

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

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

这个问题没有任何一个正确的答案。有多种方法可以解决这个问题。这一切都取决于您的要求。

答案 2 :(得分:1)

您的抽象定义了实现者必须遵守的合同。这超出了实现具有正确签名等的方法。违反它意味着打破liskov替换原则,即要求微妙或不那么微妙的错误。

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

话虽如此,我说在你说的案件中没有实际的区别。是的,这些实现在语法和语义上都是不同的,但其他类只依赖于IFace,对吧?说真的,如果已经有抽象的话,没有理由依赖具体的实现。因此,没有什么可以阻止任何人为IFace创建新的实现并传递它。