Part 1: Why bother?
First, why do we use properties? The most common answer I hear is “future-proofing”. Specifically, people are concerned that they may need to change the behavior of a property, and don’t want to break existing consumers.
I understand the concern, but I don’t subscribe to the practice 100%:
- YAGNI – Most of the properties I write, I won’t need. They just go to waste. It’s PrematureGeneralization.
- I’ll probably build some other part of my API in a way that I have to break the contract when I update, so the properties won’t help.
- If my class has default accessibility (internal), then it’s trivial to recompile every consumer, so no future proofing is necessary.
- It takes a simple idea (a field in a class) and makes it take 6 lines. This clutters my code.
- It’s not the Simplest Thing That Could Possibly Work.
If you’re a library vendor, things are a bit different. You’re selling upgradeability, as well as functionality.
Part 2: a simple approach
Just use public fields.
Wait, doesn’t that violate Encapsulation? Only a little bit. If you say “I have a public property call Foo, and its type is int”, that’s not much different than “I have a public field call Foo, and its type is int”. What are you encapsulating, exactly?
Wait, doesn’t that open a risk that someone might set the value, when they shouldn’t? Yeah, but it doesn’t scare me much. I rarely find myself writing code that manipulates the values in other objects. It’s bad behavior on my part, and rarely necessary. If it does happen, and it’s a problem later, I will fix it then.
Wait, I’m still worried about rogue setters. Fine, use ‘readonly’. I do:
Now Foo acts like a property with only a getter, and has the same syntax. If, someday, I decide it needs to be a property, I don’t have to modify my consumers. (I do have to rebuild them if they’re in a different assembly.)
I now don’t have to worry about accidentally changing a value, even in my own code.
Also, I’ve noticed a certain pattern. Typically my fields are either “identity of the class” or “working data of the class”. I can see which is which by marking the “identity” fields readonly.
Part 3: lazy load
Sometimes you want to calculate something on-demand, and then cache the value. For example, you might write:
If you take this approach, you now need to be careful to use the property inside your class, to work with the correct semantics.
I had said I would extract a new class encapsulate this behavior. Here’s what that might look like:
Note: I’m violating the Law of Demeter by exposing the Area as public. Consumers must use “mySize.Area.Value”. I think I should probably make it private, and use a public field:
Part 4: Lazy<T>
I’m thinking I could write a generic class to implement this type of caching in a general way. Do you want to take a stab at it, and post your result in a comment or on your own blog?