Func Funkiness

Func<> and Action<> are the fire beneath the almighty Linq, the savior of inline collections querying in C#. They allow the programmer to pass in a function as a parameter to a method, which can then use the behavior as an object–in other words, functions can be objects. It’s quite a powerful concept, and it is a fundamental building block of Javascript. Used in the right manner, it greatly enhances the usability of the language. Used in the wrong manner, it becomes a debugging nightmare.

Ok, nightmare is a little strong. But I do feel a bit of rage when I come across a class that is full of Funcs used as properties. Here’s a short list of why:

1) Debugging. When I want to find out why something behaves the way it does, I want to be able to F12 (or right click -> Go to Declaration) my way to the parent function or object until I can say “oh, this chain of ambiguously named variables (which is an inevitability in our trade) comes from a function called MultiplyTheseThingsTogether(). Now I know what the value means–a composite of the values from TableA and TableB” and so on and so forth. When I see a Func, I now know that I have to Shift-F12 (or right click -> Find Usages) on the setters to find all the possible places where the behavior is being defined and make my best guess at which one is relevant. I can use the call stack to trace my way back up, of course, but what if the program isn’t running or I want to go up a different path in the stack?

2) Runtime ambiguities. On a related note, since the Func is a property, it can change at runtime. This leads to situations where it is difficult to determine where a value comes from if a Func has been redefined since the breakpoint was hit. And if it hasn’t been redefined multiple times, it brings up a question as to why Func is necessary in the first place.

3) General OOP ideological issues. In general, an object should not contain behavior which it does not control. This is directly at odds with using Func as a property.

While Func can be a valid tool in the developer’s chest, the vast majority of situations in which I have seen it used in the wild (aside from Linq, which tends to make it invisible anyway–as I believe it should be) use it as a crutch for poor design or a band-aid for legacy systems. The latter can be a valid excuse, as massive refactors can be expensive and entirely unnecessary given adequate performance of existing code, but the former is where this post is directed.

For the sanity of developers that come after you, please do not use Func unless you have a good reason to do so. There are few.