5

What are the advantages and disadvantages of having a readonly field compared to having inheritors implement an abstract getter-only property (using C# as an example here, but I guess that doesn't really matter much).

Here are both ways to do this:

  1. readonly field; inheritors have to inject the value in the constructor

    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. abstract getter-only property; inheriters have to implement the property

    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; } }
    }
    

Both implementations expose a public int Field getter-only property which does not change.

However, I can see the following differences:

  1. The value of field is bound to each instance and there's nothing preventing inheritors from allowing to receive the value in their constructors themselves (public Impl(int field) : base(field)).

    Being bound to an instance, memory for the field is required for each single instance. Which might not be a big deal, but it's definitely something to keep in mind.

    The conveyed intent is: the value can only be set in the constructor and cannot change later on (leaving aside reflection).

  2. The (returned) value of Field is bound to each type, but there's nothing preventing inheritors from generating/calculating the value each time the getter is called, potentially returning a different value each time. public overried int Field { get { return DateTime.UtcNow.Second; } }

    Memory is only required "in IL", since the value is (usually) not stored anywhere, but always computed before being returned (resulting in a load instruction and nothing more).

    The conveyed intent should be: the value is bound to the type (and shouldn't change between calls, but there's no way to force that, right?). But rather the intent comes across as: you need to provide this property, I don't care how you implement it and which value it returns.

Are there any crucial differences I'm missing? Is one preferred over the other, or is it required to decide on a case-by-case basis?

I guess I'm looking for a construct/pattern/language feature which binds readonly (constant) values to a type, but expose the value at the instance level. I know that I can use static fields in each inheriting type, but there's no way to enforce this from a common base (or interface). Furthermore, static fields cannot be called when having only a reference to an instance of this type. Thoughts? I'm happy to receive answers in different programming languages

knittl
  • 246,190
  • 53
  • 318
  • 364
  • 1
    Both approaches can be "cheated" be derived classes. If the laws and rules around that field should be set in stone, you must seal it from any out-side influence. You could generate it according to StackTrace, getting the calling type, and what not. But I honestly think this would be an overkill. Your base class provides a certain functionality, and derived classes should extend that functionality - not break it. Any inherited class can be torn to bits if you really want to. Any more than that is probably opinion-based (not that this wasn't) – SimpleVar Apr 23 '15 at 06:25
  • You can't force either of them to be a constant value. Instead, you could have a well documented API which describes the purpose of the field. Why do you need to enforce a constant value with that field? – Yuval Itzchakov Apr 23 '15 at 06:26
  • @YuvalItzchakov: enforcing the constantness by means of the compiler can save a lot of headache. Sure, good API documentation helps, but let's be honest, who reads documentation anyway? ;) – knittl Apr 23 '15 at 06:30
  • @knittl If I'm implmenting some API I need to use, I definitely read the docs. I dont think that should be underestimated. – Yuval Itzchakov Apr 23 '15 at 06:35
  • Even if one does not read docs, I believe that most coders inevitably read the Summary. – SimpleVar Apr 23 '15 at 06:42

3 Answers3

3

There is one crucial difference between pattern 1 and pattern 2 you have given.

Pattern 1 does not allow to return a different value once class is constructed because base class takes field only in constructor.

Pattern 2 allows child classes to return different values at different times. Basically - there is nothing enforced from base class if child class decides to override.

Thus - it really depends what you want to achieve and your domain logic.

Regarding the intent you are trying to achieve - in my opinion - one of the ways to tackle the implement the intention is declare a virtual method (something like getReadOnlyField() in base) rather than a read-only property. Then - child classes are free to override the virtual method - if they do not override - base implementation will still be enforced.

There cannot be any one right answer to this question. There will be multiple ways to resolve this. It all depends on your requirements.

murtazat
  • 399
  • 3
  • 12
  • Thanks for your answer. I do not have a "base implementation". The base class is incomplete without implementation (and only the implementation can know the actual value). Also, the difference I mentioned regarding the storage (heap vs. IL) is important, I think (The memory shouldn't be the problem, but it's still a difference that is important) – knittl Apr 23 '15 at 06:51
  • Also look at - http://stackoverflow.com/questions/10101763/why-doesnt-c-sharp-support-const-on-a-class-method-level – murtazat Apr 23 '15 at 23:45
2

I believe that readonly-fields and abstract-getters are two completely different concepts. The readonly-field is all about how the field should be used within the class it is defined in.

An abstract-getter is all about the interface of the class. It does not put any restrictions on how the variable is used, but it forces all class inheritors to implement the getter in order to meet the interface.

The actual question should be where to locate the public getter of the public int Field property; should it be on the base or on the inheriting class? The answer (in my option) depends on whether the base class has to know the actual value of the Field property. If so, place it on the base, otherwise just force all child classes to implement the property getter.

Pieter
  • 103
  • 4
1

Your abstraction defines a contract that implementors have to comply with. That goes beyond implementing methods with the correct signatures etc. Violating it means breaking the liskov substitution principle, i.e. asking for subtle or not so subtle bugs.

I can understand if someone feels the contract must be enforced somehow, but in the end you cannot enforce complying with LSP. You can only make the intention as clear as possible by using proper documentation and usually unit tests which document behavior as well. And keep in mind that developers usually don't violate contracts or LSP on purpose. If developers have malicious intent, all bets are off anyway.

That being said, I'd say there is no actual difference in the cases you stated. Yes, the implementations are syntactically and semantically different, but other classes would only depend on IFace anyway, right? Seriously, there's no excuse to depend on concrete implementations if there already is an abstraction. So nothing stops anyone from creating a new implementation for IFace and pass that around.

bstenzel
  • 1,231
  • 9
  • 14