Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Readonly field vs abstract getter-only property

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

like image 751
knittl Avatar asked Apr 23 '15 06:04

knittl


1 Answers

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.

like image 95
murtazat Avatar answered Sep 20 '22 15:09

murtazat