Building Weird Light Switches – Out and Ref
I was talking with someone about possible approaches to an API the other day. He asked me if I’d favor a method that took a parameter and had a return value or if I’d prefer a method that was void and took two parameters but with one as a ref parameter. My immediate, knee-jerk response was “I don’t like ref parameters one bit, so I’d prefer the former.” He looked at me with a bit of surprise and then kind of shrugged as if to say, “whatever, weirdo” and went with former. To him it was six or half a dozen.
This made me wonder whether I’m being dogmatic and rigid in the way I approach software, so I spent some time thinking and reading about ref parameters and their cousin, the out parameter. For those of you not acquainted with C#, both of these are ways of passing parameters into methods with the main difference being whether the called method must change the value passed in (out) or whether it can optionally do so (ref). Another way of thinking of this is that you would use out when the initial value of the parameter does not matter and ref when it does.
Here is what the syntax looks like:
int myValue; //This wouldn't compile, so don't do it: AddOneToValue(ref myValue); SetValueToTwelve(out myValue); Console.WriteLine(myValue); AddOneToValue(ref myValue); Console.WriteLine(myValue);
In this case, you will see printed 12 and then 13 on the next line, assuming the methods do what they say they will. (This gets even screwier if you’re a Java programmer, in which case you need to create a wrapper class or use int or something since ints are immutable and parameters are always passed by value even if objects are accessed on the heap by reference).
What Does the Internet Think?
Usually when I react strongly and then want to justify my reaction, I go see what others have to say, preferably those I respect. The estimable Jon Skeet has this to say:
Basically out parameters are usually a sign that you want to effectively return two results from a method. That’s usually a code smell – but there are some cases (most notably with the TryXXX pattern) where you genuinely want to return two pieces of information for good reasons and it doesn’t make much sense to encapsulate them together.
In other words, avoid out/ref where you can do so easily, but don’t go massively out of your way to avoid them.
In the answer below his, Ryan Lanciaux raises an interesting point when he says that “[out/ref parameters] basically add side-effects to your code and could be a nightmare when it comes to debugging.”
So, two take-aways here are that having a method return two distinct results is a code smell and that method side effects tend to be a problem. On the flip-side of the argument mainly seems to be somewhat of a pragmatic, duct-tape programming kind of argument that sometimes the purist approach just isn’t worth the effort and potential awkwardness. The iconic example of using out parameters is the T.TryParse(string, out t) methods in C# (which I really don’t like, but I’m trying to be suspend my bias for the sake of investigation).
Next up, here’s what, well, MSDN has to say in explanation of a static analysis design warning that they raise entitled “Avoid out parameters”:
Passing types by reference (using out or ref) requires experience with pointers, understanding how value types and reference types differ, and handling methods with multiple return values. Also, the difference between out and ref parameters is not widely understood.
Although return values are commonplace and heavily used, the correct application of out and ref parameters requires intermediate design and coding skills. Library architects who design for a general audience should not expect users to master working with out or ref parameters.
There’s a certain irony to this, but I definitely understand the point. The irony is that the same outfit that put these features into the language raises a warning telling you not to use them. Don’t get me wrong — I understand that this is akin to making computers that can be taken apart while warning users not to do so, since many of them aren’t really qualified — but the irony amuses me nonetheless. It’s also interesting that MSDN seems to think that pointers and reference vs value are “intermediate” language features. Perhaps the fact that I cut my teeth on C and C++ as a wee programmer is showing, but… seriously? Intermediate?
At any rate, the consensus on the subject that I’ve seen at these and a variety of other blogs and stack overflow posts seems to be that out/ref parameters are generally to be avoided… except when they’re sort of unavoidable, either because of interop concerns or because you really want (need?) a function that returns two or more things.
Do One Thing
But isn’t a function that does two things a violation of the Single Responsibility Principle of SOLID fame, applied at the method level? And aren’t out/ref parameters, pretty much by definition, side effects that constitute violations of Command-Query Separation, a paradigm in which methods that mutate state (commands) are separated from methods that retrieve information (queries) and ne’er the twain shall meet? I mean, any method that ‘returns’ two values is, well, doing at least two things and any method that mutates object state and kicks back a ref/out parameter is serving as command and query.
But what about methods like the obtuse ones above, SetValueToTwelve() and AddOneToValue()? Those are void methods that mutate only the out/ref parameter. They could be made static and rewritten as int Return12() and int AddOneToValue(int value) without altering their purpose or effect. So, they’re not really violating SRP or CQS, right? They’re just slightly more awkward versions of familiar APIs.
But that really hits home with me. Why do I want something that’s either slightly more awkward or a violation of some very helpful clean coding practices? I mean, we’re not really shooting for the moon there are we, if something is at best somewhat awkward and at worst a code smell. Why do it at all? Methods should pick one thing and do it (or return it) and should do it as non-awkwardly as possible.
What If Light Switches Worked This Way?
I like to think of our task as programmers in terms of abstractions (in case you hadn’t caught my series on abstractions, feel free to click that tag and see me harp on it in a lot of posts). One easy abstraction for me to relate to the world of programming is turning a light switch on and off. This is a classic case of a class Light that exposes a command SetOnValue(bool) and exposes a readonly property, bool IsOn. So, as in the real world, I move the switch up or down and then separately observe the results. (Let’s ignore for argument sake that it might be better to model “Switch” and “Light” as separate entities).
This is a great example of Command-Query Separation. Toggling the light on or off is a command, and looking at the light to see whether or not it’s on is a query. But, let’s blur the lines for a moment and rewrite this so that there is no readonly “IsOn” property. Instead, SetOnValue(value) will return a boolean indicating whether the light is on or not. So now, we have a switch that also acts as the thing that tells us whether or not it’s on — our wall switch also just became a light. Now, when you toggle the switch, the switch itself glows to give you feedback. Weird.
But, it gets weirder. Instead of having our SetOnValue() function return a bool, let’s feed it a ref parameter. On the way in, we’ll indicate the value we want, and on the way out, it will indicate the value that we’re going to get. In terms of modeling the real world, ref parameters are kind of mind-blowing. You hand some external thing a piece of yourself and it alters that for you. So now, we have a light switch that I flip on with my hand, and indicates the success of that operation by modifying my hand – let’s say burning it. So, I flip the switch on, and if it works, the blazing hot bulb in the switch burns my hand (but gives off no light, since the burn is how I know it’s working). So, there you have it – a strange path from a light switch that turns on lights to one that simply injures me.
I realize that this metaphor is a touch strained, but here’s the thing: ref and out parameters are weird and counter-intuitive. It’s hard for them not to strain a metaphor that they’re involved in. Anything I’m handed in life could be represented conceptually as thing or a collection of things. Any action I take in life could be represented as a void method with 0 or more parameters. But what is a ref paramter? Where in life do I take something, set it to a way I want it, give it to something, and then have it given back to me differently? Maybe as part of an assembly line or in some weird, Rube-Goldberg kind of process, but this is hardly how normal interactions are modeled.
Ref and out are leaky abstractions in terms of code readability. They reek of code. Code involving these things doesn’t read like simple, well written prose or a nice story — it reads like a procedural construct such as the withholding worksheet for your paycheck deductions. So, like dealing with the IRS, why don’t we avoid that if we can? And, I think you’d be pretty hard-pressed to argue that you can’t.
I just recently read an article on Eric Lippert’s blog about out params and how they got into C#. According to the blog they solved a problem in .net 1.0 and after that they were implemented because of the earlier pattern. In the blog and resulting comments was the fact that F# uses Tuples as the return type of its try parse methods, something like Tuple/ Int.TryParse(string). Along with that discussion was the suggestion to make extension methods in C# to do this which will work better in Linq statements. Sorry I don’t remember the exact blog page.
I think this is the article:
I remember reading that, but not the comments. I notice Jon Skeet is there too, wishing they had deprecated double.TryParse() in .NET 2.0 instead of doubling down on what seems to be a design mistake. 🙂
And, what Eric suggests at the end of the article is what I invariably do when parsing where a failed parse is acceptable – I write some kind of SafeParse(string) that wraps TryParse and falls on the out parameter grenade so the rest of my code isn’t infected with it.
Yes, that was the post. Anyway, nice to visit this again in your thoughtful style and also revisit the command-query seperation which is still something that does not easily surface for me as I am working with code. May we all get better at what we do.
A no-brainer! I think you just discovered a weirdo 🙂
[…] So beware that you’re casting the die here more quickly and strongly than you might think. In most cases, YAGNI applies, but in this case, I’d say that you should assume you are going to need it. (And anything that stops people from using out parameters is good in my book) […]
[…] Now this thing should really be split up, so we start selecting parts of it to see what we can refactor. Ew, now we’re getting ref parameters to boot. This thing is getting even more painful to try to refactor, and we’re in a hurry, so no time for that. And to make matters worse, if you add in a few other aggregator variables this way, you’ll start to have all kinds of barriers in place when you want to pull this thing apart, such as crazy sets of out parameters. I’ve posted before about how I feel about… Read more »
I like how you abstract the problem to explain it 🙂
Great post anyway as usual.
Thanks — glad you liked it, even though the linked screenshot appears to have gone the way of the Dodo. I’ll have to see if I can get an illustration for this post.
Love the article, definitely reeks!
I also don’t see the linked “Light” image. I think you may have missed the link on abstractions where you say “…terms of abstractions (in case you hadn’t caught my series on abstractions, feel free to click that tag…”
Oh, I think I meant the tag in tag cloud at the right, if memory serves. And thanks for the kind words!
Personaly i used ref for learning experience. The only reason to use ref can be seen in windows form where you override inherited function or raised events and wanted to change variable for inner working code. Exaple: e.Handled = true; Othervise i can not see my self using ref or had need to use it.
Sure, there definitely are times where it’s forced on you by an API and there’s nothing you can do. I get that. I just try to avoid exposing methods like this to other people.
PInvoke has “infected” much things on dotnet because you need to interop with smells that come from the C language. without ref you cant pass pointers.
Thanks for the article, its very valuable. But I find the RL analogies pretty artificial and to be honest, a little nonsensical. “Where in life do I take something, set it to a way I want it, give it to something, and then have it given back to me differently?” Frequently? Yesterday as I drove my wife to the train station I handed her my wallet . She took out my weekly train ticket (so she could use it) then handed the wallet back, now minus the ticket. Two weeks ago I dropped my car at the garage, then picked… Read more »