1

Some of my classes use a protected readonly field that represents a logger. Below is some sample code -

public class RunJob {
    protected readonly ILogger logger;
    public RunJob(ILogger logger) { this.logger = logger;}
    public virtual void ProcessFiles() { 
        logger.Log("Start");
         ... 
        logger.Log("End"); 
    }
}

Having gone through many articles/threads on properties vs fields, I realize that protected fields are considered bad but I'm wondering if I will gain any real practical benefits by making logger a property. Considering my use case above, is there any real difference between the below 3 -

  1. protected readonly ILogger logger;
  2. protected ILogger Logger { get; } or perhaps (protected ILogger Logger {get; private set;} - which is readonly only for subclasses)
  3. #2 backed by a private _logger object

In my application, #1 works well enough - The logger object is initialized once in the constructor and is then used throughout the class (and its subclasses), this class and its subclasses do nothing other than calling logger.Log(...) and no one except for this class and its subclasses cares whether it's a field or property.

I have gone through this thread but many questions remain unanswered -

  1. One benefit that could've been applicable here is that the properties can be overriden, but in C# they're sealed by default (unless properties work different than methods). So, unless the developer intentionally makes a property virtual, this benefit is not applicable.

  2. Are there any performance benefits to be gained by using a readonly field? Surely, the compiler etc would have to work a little extra to make readonly properties work?

I do see the overall benefits of properties over fields in principle and even though it's just one line of change in my case, I wonder if I'm doing it just because it's a new cool fad or whether it really truly applies in this case?

Community
  • 1
  • 1
Achilles
  • 1,099
  • 12
  • 29
  • get-only accessors like protected ILogger Logger { get; } can be assigned in constructor only, behaving in a similar way as readonly fields; when you have protected ILogger Logger { get; private set; } it can be assigned anytime from the same class. – Heorhiy Pavlovych Jan 14 '17 at 11:35
  • protected then the class can be extended and the variable set to something else at any time. a read only can only be set by constructor base or extended class. – Thomas Andreè Wang Jan 14 '17 at 11:38

3 Answers3

3

I'd probably not overthink the issue - it is not that important in the greater scheme of things.

Yes, properties are much more future-proof and flexible:

  1. You can set its value internally if it needs to be changed during instance's lifecycle protected ILogger Logger {get; private set;}.
  2. You can make it Lazy : private readonly Lazy<ILogger> _logger; protected ILogger Logger {get {return _logger.Value; }}.
  3. You can proxy it to some other object protected ILogger Logger {get {return _someParentObject.Logger; }}.
  4. You can even make it dependent on some other piece of state protected ILogger Logger {get {return IsA ? _a : _b; }}.
  5. etc.

, but readonly protected fields aren't actually that bad:

  1. because they are readonly, unlike mutable fields that are much much worse. Obviously, it is even better if they are also immutable or stateless(like it is probably in your case).

So: either make it a property so you don't have to worry about any future changes, or just leave it as it is - KISS and YAGNI. Again - it is not so important to dwell on it too much.

Community
  • 1
  • 1
Eugene Podskal
  • 10,270
  • 5
  • 31
  • 53
  • Thank you, KISS and YAGNI works. But I'm curious about what you said about properties being future-proof and flexible and potential abuse of fields. Do you mind elaborating a bit on the scenarios when the protected readonly fields might break things? – Achilles Jan 14 '17 at 11:50
  • @Achilles I removed mention of `potential abuse of fields/inheritance` because protected properties seem to have mostly the same issues - http://softwareengineering.stackexchange.com/questions/134097/why-should-i-prefer-composition-over-inheritance. – Eugene Podskal Jan 14 '17 at 12:21
1

I would not care about performance difference in this case at all because it's so minor unless you have a really really good reason for it. You can read about premature optimizations here e.g. Otherwise I don't personally see a point of creating protected properties in this case. The reason for a property rather than a field is that you can control the assignment, which in this case you don't really need, since planned use is to just read it anyway. Though in theory children could override logger and it's not good probably. But in practice I doubt this could happen.

Regarding 2 vs 3, 2 is just a syntactic sugar over 3 as far as I know. I guess it will even be compiled the same, you can check that.

Community
  • 1
  • 1
Ilya Chernomordik
  • 27,817
  • 27
  • 121
  • 207
  • Thank you. Not sure I understand what you meant by "Though in theory children could override logger and it's not good probably". Could you please elaborate a bit? – Achilles Jan 14 '17 at 11:52
  • I meant that if you have protected field, then children can assign it (set, not only use), compared to a property with private set. Otherwise if it is readonly that's limited to a children constructor and that is not really a big deal (unlikely children will mess with it). I really usually limit the use of inheritance and like composition better, so I just use private _logger, when I do use inheritance, then protected _logger is just fine and I would use it. – Ilya Chernomordik Jan 14 '17 at 16:25
-1
protected readonly _logger;

and

protected ILogger Logger { get; } 

can be assigned in constructor only.

But

protected ILogger Logger {get; private set;}

is readonly only for subclasses, but can be assigned from any method in the same class;

when you do have backing field, it is more lines of code; I personally would prefer using

protected ILogger Logger { get; }

to avoid exposing field to subclasses, which is considered as a bad practice