That's not quite it. The real rule is:
Define all members as restrictively as they can to be.
So, if it can be private
then it's private
. Otherwise if it has to be internal
or protected
to work, then it's internal
or protected
respectively. Otherwise if it has to be protected internal
to work, then it's protected internal
. Otherwise it's public
because it has to be public
to work (something outside of the assembly that doesn't inherit from it is going to have to access it.
Now, all that said, it is generally true that we'll have mostly private
fields and mostly public
properties.
Let's consider if we do have a public
field. That's valid, will work, and is even useful in very limited cases. However it's also more brittle. Having it public
lets any other code set it to any value, no matter how stupid. That's okay because when we make a field public
we're saying "there are no stupid values".*
But what if we suddenly realise there are some stupid values, or we need to do something every time the field is changed, or we need to work with some property-only data-binding. Here we're going to have to turn the field into a property, and that's a breaking change; any code using this class will have to be re-compiled, which isn't that big a deal if we're the only person using the class, but is if it's in a library being used by others.
If it had been a property however, then we'd just have had to turn an auto-property { get; set; }
into a property with custom implementation { get { /* do stuff and return value */ } set { /* do stuff */ } }
. From outside the class it looks like nothing has changed and we haven't broken anything.
Therefore 99.99% of the time if you are going to make a field public
you're better of using a property there, just in case.
On the other hand, if we've a private
property that has no logic, then the getter and setter methods do not give us any benefit. There won't be any cost, but they're pointless. So we just use a field instead. If we end up having to use a private property in the future that won't matter because the only code that has to be recompiled is the very class in question, and we were re-compiling it anyway.
As such we end up with the case where almost all fields are private
and almost all properties that don't have any custom logic are protected
or public
.
*Or it's a private
or internal
class and we promise from the outside not to set it to something stupid, but it's better to enforce promises than to make them if you can.