I've debated this question internally (Tony, what do you think?) and with others. And others have asked me: “when do we?” or “why do we?”
While I can rattle off some known facts and opinions why one over the other, the science is missing. That's what we're doing here: The Science.
Known Facts
First, let's get the irrefutable truths out of the way so that we can move on from them. If any of these are deal breakers for you, you may get off this train now.
- Properties will access a field eventually. A backing field is used for properties. Period.
- Properties (because of the above) have some performance hit vs. fields.
- Fields and properties can be accessed by a consumer - they can set and get.
- Fields are either public or private. You cannot have a public GET and a private SET.
- You can have a readonly field, but then it can only be initialized and not changed within the class itself - so a little different.
- As a consumer, properties allow you to GET but not SET (or the reverse)
- Fields do not exist on interfaces. If you're building an interface, you need properties.
- Fields are not serialized. Your ToJson() extension method will not include fields. (Which is also a really need trick to keep serialized data [JSON] cleaner. Yes, I know we could use an attribute, but I'm just saying.)
Intent
This one is going to be small because I don't know and have not spoken with the C# designers. But, based on things like interfaces and the way the BCL is used, Properties are meant to be what is exposed to the world and fields internal to the class/object. In theory, this is a sound argument for not having public fields, ever and always use Properties. Doesn't address private properties, but there's some intent. Of course, why aren't fields private by design and not allow public ones? I dunno.
Culture
This should not and cannot be underestimated. The culture of coding and developing has ensured that we can be super productive by lowering the cognitive overhead of thinking though what something might mean. Knowing that an underscored variable (_daysToWin) in a class is a field is a huge win. For example, in a normal codebase, a developer would almost instantly know what these represent:
- ExpirationDate (class property)
- m_expirationDate or m_ExpirationDate or _expirationDate (this one has changed over the last 10 years from the “m” for “member” to its removal) (class member/field)
- expirationDate (local variable)
Being able to distinguish a variable that easily is gold in developer hours. Therefore, the culture of the developer's world does and should have an impact on your choice. However, this doesn't mean you can't change it or do it differently. That would be the worst thing that could happen to humanity (not thinking you could change cause “that's the way it is”). But be aware of the cost and then determine if it's worth it.
Opinions
Now for the infamous opinions. I will state some I have read/heard and my own. Remember, opinions are like assholes, everyone has one (but don't be one about it).
Mine
- Reducing Cognitive Overhead by not changing what everyone knows is huge. I'm a huge proponent of conventions. More efficiency, Less bugs. Really need to know. For this argument, I am not sure the change is super huge. I would be willing to say that even a subset of a project with a different set of rules than the whole project is worth it if you can eke out 5% improvement by using fields instead of properties (I am not saying that's case because we don't know that's the case [yet], but if it were…).
- IDE Bonus for Perf Win - VS Professional tells you how many times a property is being accessed. It also allows you to use code lens to go to the code and see it in a small window. This is such a savage OP move. Why? I have code where some time after (years???) I see that a property is never accessed. Why is it there? Move it for a huge gain! Huge? You scoff? I use NPOCO (and you use DAPPER, I know, and YOU use EF, fine), these libraries will auto generate T-SQL and hydrate a class based on the properties. If you have a class that has 20 properties but you only access three of them, you can reduce the T-SQL work, the data on the wire, the hydration work, the memory impact, et al. by deleting (or commenting out) properties that you haven't used! Do it. And yes, Mr. Contrarian, you can use a DTO and get the same benefit. That's not the point. This can manifest a few different ways, but I think you get the gist.
Performance
And lastly, to maybe get you off the fence. What's the performance here. If you think about this, this might be the deciding factor. If the known facts are not impacting your scenario and you're doing some code where performance matters (tip: it always does), then maybe using fields when possible is ALWAYS the correct answer. Let's find out.
After extensive research by reading others' articles and SO questions and answers, it's a little muddy. This is why:
The JIT (not compiler) can inline a simple get/set into a field. Basically, if the JIT knows that you're not doing anything special inside the get or set, it basically turns it into a field. Sweet win!!. But not always. There are some cases when you think it would, but doesn't…this is more anecdotal that tested, since the scenarios where one can happen and not the others is up to the innards of the language.
Also, the amazing C# team is always doing improvements to the language. And while this might have some impact today, it might not tomorrow.
Wrapping this up…sorta
This has been in draft for almost a year. I wanted to write up others' opinions, do some benchmark testing to see what we're really talking about and polish this up. Honestly, I don't have the time or energy. So I'll share this as it is. And close with the following:
There might be some performance gain and if your project or team is indifferent, you should probably use fields to keep things as performant as possible. I would not use fields for a class library, or public class consumed by others, etc. but a bump is a bump 🤷♂️