Channels ▼
RSS

.NET

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.


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.
 

Comments:

ubm_techweb_disqus_sso_-52eff9fbd0a357108aab8471f4b82b70
2013-04-30T21:36:52

It seems to me that the motivation for properties arose in the client-server era of the 1990s. Creating GUI front-ends was difficult, so Microsoft tried to make it easier via Rapid Application Development (RAD) tools, also referred to as "components." These are GUI widgets that the programmer picks from a palette, places on a form, and then configures by entering values in a property editor. Properties were added to the language to facilitate the creation of custom components -- to configure the automatic generation of property editors.

RAD tools usually do not easily support the creation of an object-oriented hierarchy of GUI widgets, so it's not always the best way to build GUIs -- but often it's the fastest and easiest way to solve a problem. When you use a property-based RAD environment, then you have to use properties.

In other cases, the use of properties is probably both unnecessary and sub-optimal.


Permalink
ubm_techweb_disqus_sso_-6b3896ba327bba0e0decd376ef026f2d
2013-04-25T18:01:13

I agree with you. However, I noticed that many developers (especially those who work mostly in the open source space) dogmatically cling to the mostly untrue assertion "that 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." Have you notice that too? Good perspective on properties though.


Permalink
ubm_techweb_disqus_sso_-c972cfea548068c269e7b45f34fefd9b
2013-04-25T15:30:57

Hi Allen, This appears to be a re-post of an older article, but I very much appreciate your point of view. In fact, I'm sold.

Properties have always been a bit of a problem for me. The fact that you can put as much code in them as you want completely violates the principles of properties. I've seen them used in horrendous ways (even with Microsoft's examples). Properties are useful for somethings, but they're not required to get a job done.

I've subconsciously been searching for a reason to get away from Microsoft's "Best Practice" of using properties, and you've provided a very reasonable explanation. Thanks for the time to put this article together.


Permalink

2013-04-25T15:29:27

There are many things wrong with the article, but let’s start with the first example, you are in violation of CA1065 which if you had been using Microsoft's Code Analysis Tools ("Following the Microsoft Party Line") could have been configured to cause a compiler time error.

The problem is those who claim to "Follow the Microsoft Party Line" rarely ever do and instead speak from a place of ignorance (such as a Java Developer converting to the "Microsoft Party Line") and are unfamiliar with the tool-chain available to a seasoned Microsoft Developer. They then proceed to bemoan problems which were resolved years ago. The author of this article appears to be one of them.

The second issue is not a problem unique to C# or "The Microsoft Party Line". That issue is present in any code base, even Java.

Furthermore there is a huge difference between: public int x {get;set;} and public int x; As was pointed out to Jeff Atwood when he made the same complaint back in his August 7, 2006 blog post titled “Properties vs Public Variables. The short of which is: Reflection works differently, you can’t databind against a variable, and changing a variable to a property is a breaking change.

The remainder of the article appears to be a complaint against bad design, something that is present in any programming shop.

I would wager this is pure and simple flamebait and I'm feeding the troll here.


Permalink
rwgrwg
2013-04-25T15:24:00

Any of my neighbors can buy a hammer, nails and wood. But I would not hire them to build my house. And I’m not going to call for removing
hammers from the store because there are nonprofessionals out there using them.
The problems described here can be addressed
by hiring professional developers instead of removing tools from a language.


Permalink
facebook-100004425644239
2013-04-25T15:05:18

I think several comments are based on an incorrect assumpttion the author put forward.
One of the aspects of Money is not that it supports addition, it is that it supports Pooling. I can have several currencies in my wallet but thay only have a value in US Dollars based on a particular moment in time. Money within a particular Currency can be have its values added to pool but not other currencies.
So if I want to Add 100 Euros to $100US, I need an ExchangeFactor to convert one to the other or a 'Wallet' of some type to contain BOTH currencies. The first allows me to convert immediately for purposes of valueation, the second allows me to defer this to a later time. Money is really only a base class for various currencies.


Permalink
ubm_techweb_disqus_sso_-ca8fdb55cb9827ff994af2aad8c3c644
2013-04-25T14:20:26

Allen -- seem to be getting long in the tooth. I have been reading your balanced and sometimes not so balanced but still eminently sane pieces. This is jumping the shark,


Permalink
ubm_techweb_disqus_sso_-0133f94edc401a63d93eef35f0e53dc7
2013-04-25T13:10:01

One can refer to http://www.javaworld.com/javaw... for another explanation of the many oo-issues regarding getter/setting aka property methods. I personally think the addition of properties further hides these issues.


Permalink
davidjmarcus
2013-04-25T12:46:57

So, you have fully encapsulated Money. Great.

But at some point the user of Money wants to know how many dollars (or pounds, or pesos, or lires, or...) they have.

So the Money object has to at least have some methods (or properties?) to divulging its value.

So these properties will return some native data type (float, decimal, ...).

Now you have the same problem you started out to solve. The initial coding might have returned the amount of money as a float but now it returns it as a decimal. As you correctly point out, when you make such changes something breaks.

That leaves me to conclude that an instance of Money should never divulge how much it contains but be fully capable of manipulating the internal amount in any way the specs require.

SO, in the ideal world, Money becomes a write-only object (with constructors that accept a currency amount/type in all conceivable formats so its public signature can handle any format the caller insists on using).

Mmmm... its a lot of work for an object that I can't get any money out of :-)


Permalink
google-8f12f627ffe6c517ece7e77401da2ad3
2013-04-25T12:12:36

To your first point regarding exceptions, I believe Java did us a much greater service than Microsoft: Because the language requires that a method call (or property getter/setter if it were used in this scenario) that throws an exception either use a try/catch or declares that the consuming method in the call stack throws an exception. While it's a pain to do sometimes, at least you know what errors to expect.

Microsoft does a good job documenting exceptions methods or properties throw, but no such standard is enforced on third party providers.


Permalink
ubm_techweb_disqus_sso_-c0a40ba09b172f87b543e6590018b42d
2012-09-28T04:42:18

This article echoes my sentiment. I see properties as an enforced (though not theoretically) anti-pattern.


Permalink
ubm_techweb_disqus_sso_-5c471a50164a7b307706e8eba25b8488
2012-09-11T23:04:49

Without knowing anything about the semantics of a particular set of code it is possible to prove that the law of Demeter produces more code in a language such as C#. We need only look at the extra code required to wrap the member call variations. For example, if a is an instance of class A, a.B.C() violating LoD, exposes all members of B. If we wrap them all, then all members of B (potentially) need to be on A: a.BC(), a.BD(), etc.

Again I think the concept of layers is under-used and should be taught, but the LoD is not always well adapted.


Permalink
ubm_techweb_disqus_sso_-5c471a50164a7b307706e8eba25b8488
2012-09-11T22:57:45

There may be no field behind the property. They are encapsulations. For example, it is possible to implement a class with many properties, an instance of which has zero memory backing the storage of any specific property, until the property is set to a non-default value. This is common in components that must have an easy to discover set of properties, but at the same time need to remain scalable when instantiated in the millions. Underlying is some sort of dictionary.

In .net, attributes are used extensively by the property grid, but most of those attributes apply to properties. It is also possible to create "pseudo" properties... but the real point is, it is easier for the developer to learn how to use the component when there is a correlation between the property grid and the component API.


Permalink
ubm_techweb_disqus_sso_-b2af94f3e93e20d5f9d69447ba03c2dc
2012-09-08T04:18:16

I agree on two things, one I just learned by googling you,
both XML and getter/setters need to die in a fire.

However, for the rest of your article, I suggest that if a person finds these sorts of problems while working with properties, they should stop sucking so much at software engineering.

Money.Value is a terrible property and your encapsulated version is great but that's because Value should have never been put in there in the first place. There would not be dozens or hundreds or places needing code changes, because here's what you really do with properties
Money.Pound
Money.Dollar
Money.Euro
Money.Yen
(I'd still include an explicit converter with a big switch statement, but my first real language was C so I can't help it).

"If the compiler cannot make at least a few fundamental guarantees, then the language is flawed."
hahahahahahahahahahahaha
oh god, if only
You should try ATS or a functional language.
And even assembly isn't safe because the processor can reorder instructions.


Permalink
ubm_techweb_disqus_sso_-969365635d3099379be6bda30f723a78
2012-09-06T12:41:56

The first example seems a red herring. Only a useless developer would write one line properties to encapsulate such exception-fragile calls. Not a worry for me, because he wouldn't work for my company for long.

The second example professes Academia without concern for pragmatism. There is a large cost of entry for an initial implementation of code-bloated abstract types. I prefer my initial implementation to stick to simple basic types and readable code (e.g. double over a "Money" class). this easier to implement, read, and DEBUG than 10X the code spread across abstract classes. IF and WHEN the application becomes successful and needs to expand in the future; I can invest my invaluable time at that point to simply refactor into abstract relationships when neccessary, the compiler as my copilot. Furthermore, at this point, the basic implementation is working and expanding in steps it into something larger is far easier than trying to do the entire ball of wax initially.

I'm writing a class right now that describes a real world entity. It needs to make available a dozen well defined basic types, types whose physical counterparts are unlikely to change in my lifetime. These types need to propegate to a myriad of unrelated APIs that require basic types or their own layers of these basic types. Why should I tie all of these types into additional abstract layers? A property gives the perfect "public data" this concept demands, while still providing capability to hook in some error checking, etc.


Permalink
ubm_techweb_disqus_sso_-71fe90849f198c1debe4b67f14243c3d
2012-08-29T19:44:17

Well, yes, it certainly does. That was kind of my point. I've found that poor encapsulation is common place. It doesn't seem (to me) to be any more or less common in C#.

I don't believe that properties encourage poor encapsulation. If you want to convince me otherwise, then you'll need some hard, crunchy numbers.


Permalink
ubm_techweb_disqus_sso_-e2aff55eddad1379a067e2de08b2be7f
2012-08-25T16:19:09

But isn't this a violation of SRP? I'd consider the lightbulb's main responsibility to produce light. If it's required to know how to screw itself in, it sudenly does a lot more. And need to knows about socket types, max force that can be applied to the socket screw and whatnot. @tennvol following the article's point of view I guess you'd implement the first one like Money.PrintValue( SomeStream ). Second one, not sure. Maybe Allen can respond.


Permalink
ubm_techweb_disqus_sso_-923c977676392cc46a28cf90977c226d
2012-08-24T14:24:43

your apps only require properties because the tools you use expect them. That's all.

The main point of the article was that there is no difference between a property and a public field (apart from the syntactic sugar used to access it). So all that's happened with your MVVM tooling is that you have to access the data via a layer of indirection that wouldn't exist if you accessed the value directly. And there is no protection, no abstraction, no encapsulation to using a property, just a bit more code to go through. You might as well hit the field directly for all its worth.

The tools you use could have accessed fields just as easily. that fact they don't speaks more for the blinkered ideology that "direct access to fields is bad, therefore we'll use properties to do exactly the same thing".

Now, if you've bodged the system by adding a "notify something" function call in each VM property, then you're really relying on exposing each field to the outside world. Instead of writing a VM class that calls the notify before updating the underlying data in the model object, you've mixed the 2 up to make a mess rather than a good OO design. MVVM isn't a particularly good pattern (the author says so).

Either way, don't confuse what the tools you use demand you do with good design.


Permalink
AllenHolub
2012-08-24T04:41:55

One of the main points I was trying to make is that properties actually increase the amount of code. That is, if you: (1) get something, (2) use it, and (3) put it back, that's more work than just asking the object that has the information to do the work for you. I think that dog.expressHappiness() is way more readable than dog.body.tail.moveLeft(); dog.body.tail.moveRight(); or even worse, dog.getBody().getTail().moveLeft();

The issue with exceptions is that you typically don't expect code like

x=y;

to throw an exception. Though it's true that a C# programmer *should* expect even a simple assignment to have ridiculous side effects, programming defensively in that world is very difficult. I don't want to surround every assignment with a try/catch in case it throws an exception, and simply looking that that assignment gives me no information about whether I'm looking at a simple assignment or something more dangerous---I have to look at the source code to do that, and the source code may not be avilable to me. When I know that it's actually a method call, I can be more on guard.


Permalink
AllenHolub
2012-08-24T04:35:42

I think of public properties as a symtom, but they're not the main issue. The real point is that an abstract object shouldn't expose impelmentation at all; but rather, should focus on the object's responsibilities. This way of thinging about objects goes all the way back to Kent Beck and Ward Cunningham's CRC cards. (Find the original paper at http://c2.com/doc/oopsla89/pap..., but there's a lot written on the subject. Rebecca Wirfs-Brock's books (she dubs it responsibilty-driven design) are particularly good.


Permalink
AllenHolub
2012-08-24T04:29:59

There is such a thing as a bad design :-), not that I'm casting aspersions on yours. Blindly following any heuristic rarely gets you anywhere useful. Of course, I haven't seen the code, but my guess is that the methods exposed by the objects weren't sufficiently abstract. That is, the designer of the code didn't think enough of the work that the object had to do, and then design an interface whereby the client object delegated work to the object that exposed the interface. Without that sort of abstraction, following the Law of Demeter won't buy you much.


Permalink
AllenHolub
2012-08-24T04:26:35

I'm not a big fan of purely "Functional" languages like Haskel, or to a lesser extent, Scala, which don't support mutable state to one degree or another. Though immutability helps a lot when you're doing things like mulithreaded systems, languages that mandate immutabilty tend not to be very intuitive or easy to use.

Re your exaple of a VB property sheet. I think an attribute would be a better choice here. For example:

[propertySheetItem] private Color backgroundColor;

VB could use the attribute to build the property sheet, but the backgroundColor would be inaccessible to the actual program. Java used the get/set idiom for it's properties, but it also supported something called a BeanDescriptor, that made the property sheet irrelevant. The basic idea was that every object had to create it's own property panel, which the design tool could instantiate and display. Of course, it's much more convenient for the design tool to build that property-sheet-display class for you. Seems like an attribute is a good compromise position.


Permalink
AllenHolub
2012-08-24T04:19:05

I wouldn't use either. Rather, I'd use

Address home = new Address("http://www.holub.com");
home.connect();
String contents = home.read();

There are actually two levels of abstraction, here: The Address class and the URL itself, which is an abstract representation of a dotted quad. In fact, the notion of a URL is a good example of why abstraction is a good thing --- you can move from a dotted-quad representation to an IPV6 variant without changing your source code.

Having said that, it's absolutely true that the closer you get to the hardware (or the operating system), the harder it is to keep the system "purely" OO. I usually solve that problem with a layered architecture that lets me use very abstract objects at the "business level," but implement those objects using the lower-level somewhat-less-abstract abstractions.

That's true even with accessor/mutator methods. For example, you might use JSON in a REST application as a means of returning a value from a server, and the class that represents the JSON will probably expose everything. In fact, I usually use public readonly fields in data objects of this sort. However, I never expose that data object for more than one level---I immediately wrap it in a more abstract object that doesn't provide direct access to the returned JSON, and then write the rest of my code in terms of the abstract wrapper.


Permalink
AllenHolub
2012-08-24T04:05:26

MVC is neither the only nor the best architecture for user interfaces. It works fine for little things like buttons, but doesn't scale at all to large complex objects, like Employees. Check out the Presentation/Abstraction/Control architecture, for example, or even MVVM (which you can implement quite nicely without exposing state, though Microsoft's flavor of MVVM doesn't do that).

I'm not sure what you mean by "bind an address to a web page." Could you elucidate?


Permalink
AllenHolub
2012-08-24T04:01:46

You said it yourself in your last paragraph. The thing that's bad is the programming practice of exposing an object's implementation. Whether you do that with properties, get/set methods, or some hitherto unimagined language feature, makes little difference. My specific issue with properties is the ease with you can viloate the "no surprises" rule, which makes them worse than get/set methods in my mind. The fact that C# does provide the syntactic sugar lures people who don't understand the issues into thinking that there's nothing wrong with violating encapsulation. Surely (I immagine them saying), if the language supports it directly, there's nothing wrong with the practice. I don't buy *that*.

I'm not sure, by the way, why you mention that a lot of code that you've seen has get/set methods in it. Sturgeon's Law applies as much to code as it does to everything else.


Permalink

Video