Zend certified PHP/Magento developer

Saying No To Properties

I was interested to find the following statement in the comments section of last month’s article on the enum pattern in C#: “I was rather stunned by the author’s casual mention (in parenthesis) that one of the things he apparently dislikes most about C# is Properties — that’s one of its strongest and most important features…” My reaction is that I’m stunned that he’s stunned. Properties (and their cousins: explicit accessor/mutator — getter/setter — methods) have been discussed in great depth over the past 30 years. It’s not as if mine were a new or out-of-the-mainstream point of view. You can choose to disagree, of course, but you shouldn’t be stunned.

Let’s look in depth at the reasons for my off-the-cuff remark.

Disclaimers

I know from past flame wars that the point of view in this article will be controversial to some, so I want to start with a few disclaimers.

I’m mostly platform-agnostic when it comes to software construction. Use what works for you. We’re talking about properties, though, and properties are solidly in the middle of the Microsoft world. Microsoft makes fine products. Nonetheless, I’ve noticed that many programmers in the Microsoft ecosystem are somewhat insular: They accept the Microsoft party line when it comes to architecture without much curiosity about the alternatives, and often assume that Microsoft “best practice” is universal across the board. It isn’t.

I am in no way arguing in this article that you should not use properties to implement code for the various Microsoft frameworks that require them. Properties are a fundamental part of Microsoft’s thinking, and you can’t possibly eliminate them altogether and still program for .NET in any meaningful way.

That does not mean, however, that you should use properties indiscriminately throughout your code. The best programmers understand the pros and cons of all of the programming idioms that they use, and make intelligent trade-offs by weighing them. You simply can’t work competently unless you understand both sides of an issue. Using properties (or any design pattern) blindly can lead to no good outcome. There are no absolutes in good design.

The point of this article, then, is to discuss the cons of properties, so that you can make smart choices about when (and when not) to use them. You already know the pros, so I’m not going to spend time discussing them here.

No Surprises

So, what’s wrong with properties?

Let’s start with the simplest issue. One of the most important basic principles of good programming is: There should be no surprises. Code should work intuitively, and you should never set up a situation where you can be bitten by side effects. It’s too easy to violate that principle if you use properties. Consider the following code.

public class MyClass
{
    public int MyProp { get{ return readValueFromConfigurationFile("myProp"); }
                        set{ modifyConfiguration("myProp", value); } 
                      }

This code looks reasonable on the surface. However, the readValueFromConfigurationFile("myProp"); call throws an exception both when it can’t read the value and when it can’t parse the value that it reads. The modifyConfiguration(...) method has similar issues. The problem lies in this code:

MyClass x;
//...
int value = x.MyProp;

I am shocked that these harmless looking assignments might bring down my server via an uncaught exception. It simply doesn’t occur to most programmers that they need to surround assignments with try/catch statements. (More to the point, I’ve seen code exactly like this many times, and I’ve never seen a surrounding try/catch.)

There are many variations on this theme. A concurrency bug in a getter or setter could put us into a totally unexpected deadlock situation if it has to use locking, for example (and a property that looks like an atomic assignment should certainly use locking to guarantee atomicity in a multithreaded application). I’ve also seen properties that make database calls, and the resulting inefficiency is another surprise.

This is dangerous stuff. A statement as simple as x.prop = 0; should not bring a server to its knees. Throwing an exception or making a database call in a property is just as bad as overloading a + operator to do subtraction. Noting that assignment to a property can throw an exception in the “class contract” (which in C# is just a documentation comment with no compiler-level enforcement) is not good enough, just as noting that + actually does a – in the class contract is insufficient. A contract of that sort is fundamentally flawed because it violates the rules of common sense.

I also don’t buy the argument that “you shouldn’t do that.” If the point of a property is to hide implementation in such a way that it can be changed without clients noticing, then I should be able to make any change without introducing surprising and dangerous behavior. The language just shouldn’t permit those sorts of changes. That is, the arbiter of the class contract should be the compiler. If the compiler cannot make at least a few fundamental guarantees, then the language is flawed. Modifying a normal method might be dangerous, but at least it’s not surprising.

I might feel a little better about this situation if C# supported a Java-style “checked exception,” which guarantees a compiler error if you don’t handle an exception. Checked exceptions would, at least, give us a real contract that was enforced by the compiler. However, even a checked exception wouldn’t address the it’s-not-intuitive issue.

I’m really making the same argument, here, that I made last month in discussiong C#’s flawed approach to enums. It simply shouldn’t be possible to pass a method that takes an enum argument some value that doesn’t correspond to any enum element. The fact that you can do so in C# is a flaw in the language; the whole point of a constrained type, after all, is that it be constrained.

OO Issues

Now let’s move on to the object-oriented design issues. It’s a fundamental OO principle that an object should be a black box. You send it messages and it does work for you. You should not know how it does the work, however. That is, an object is defined by the work it does, and the way that it does the work should be hidden. That’s what is meant by encapsulation.

The main problem with properties (or explicit get/set functions) is that they provide too much access to the underlying implementation of the object. They don’t have to, of course, but most applications of properties are not much different from public fields. In fact, I often see the idiom

public int x {get;set;} 

Many books even recommend that you use that idiom for every one of your fields! However, that’s literally no different than

public int x;

So why is the violation of encapsulation by providing direct or indirect access to fields a problem? The first issue is maintenance. You should be able to modify a class’s implementation — completely scrap it, in fact — and as long as the interface (the method signatures) does not change, the clients of the class (the objects that send messages to instances of your class) should not know that a change has happened. Over use of properties makes that level of encapsulation impossible, but when you don’t have it, the consequences of even a simple change can be significant.

If you’re working in an Agile development environment, code that’s not built to be this robust simply can’t stand up to the rigors of the development process, which mandates constant small changes — the changes end up requiring so much work that you can’t really be Agile.

Let’s look at a concrete, though simplistic (I use the word deliberately) example: A Money-class implementation:

    class Money
    {
        public float Value { get; set;}
        //...
    }

What happens when we decide that Value should really be a decimal, not a float. The notion of a property providing a way to make changes transparently doesn’t really work here because the code that uses the Money objects will, most likely, not be able to accept that particular change from float to decimal. That is, a statement like:

 
Money m; 
//...
 float value = m.Value;

won’t compile if I change the float to a decimal. It’s unreasonable to expect the code to have been written in such a way as to protect itself from that sort of change. For example, you won’t expect the client code to look like this:

 
Money m; 
//... 
decimal value = (decimal) m.Value;

Put in design terms. The class “contract” cannot stand up to obvious modification. Whether or not you follow the contract is immaterial. The contract itself is flawed.