Always Use Named Arguments

Consider a C# function call such as this:

string pathname = "...";
var lines = ReadLines(pathname, 10, true);

Is ReadLines() a poorly-designed function? Or is this just a bad function call?

If you’ve never seen the definition of ReadLines() before, you have no way of knowing what the three arguments represent just by looking at the function call. Granted, the first argument – pathname – is self-explanatory. But the other two arguments? It’s impossible to know or infer anything about them.

And when you’re reading (or debugging through) a lot of code, you really don’t want to have to rely on Intellisense (by hovering or pressing Shift+Ctrl+Space) or Peek Definition (Alt+F12) to figure out what such values mean!

Here’s the actual function declaration (contents omitted):

string[] ReadLines(string pathname, int maxLines, bool ignoreBlankLines)

And here’s how the function call can be improved:

string pathname = "...";
int maxLines = 10;
bool ignoreBlankLines = true;
var lines = ReadLines(pathname, maxLines, ignoreBlankLines);

But this is far too verbose! If you did this throughout your code base, it’ll significantly increase in size 😦

JavaScript has a nice solution:

var lines = ReadLines({
    pathname: "...",
    maxLines: 10,
    ignoreBlankLines: true

Instead of passing individual arguments, you pass in a single object containing properties for the three values. (This allows values to be easily omitted, so obviously the function should perform validation of the object to ensure required values aren’t missing.)

It’s still a little verbose, but much more readable. And it could be condensed into one line:

var lines = ReadLines({ pathname: "...", maxLines: 10, ignoreBlankLines: true });

Named arguments in C#

But hang on a sec!

C# 4 (Visual Studio 2010) introduced ‘named arguments’:

string pathname = "...";
var lines = ReadLines(pathname, maxLines: 10, ignoreBlankLines: true);

Bingo! The reader of this code knows exactly what 10 and true now mean.

Instead of using named arguments, you could follow the JavaScript approach and put the parameters into an object:

class ReadLinesParams { ... }

string[] ReadLines(ReadLinesParams args) { ... }

var lines = ReadLines(new ReadLinesParams {
    pathname = "...",
    maxLines = 10,
    ignoreBlankLines = true

It’s not quite as easy as doing this in JavaScript because you have to define the ReadLinesParams class with properties for the three values.

Or you could create an anonymous type instead, and accept a dynamic argument:

string[] ReadLines(dynamic args) { ... }

var lines = ReadLines(new {
    pathname = "...",
    maxLines = 10,
    ignoreBlankLines = true

This has pros and cons, but by using ‘dynamic’ you lose both type safety and the ability of the code to document itself (i.e. how would somebody know what to pass into ReadLines() without looking at the function’s source code?).

Personally, I prefer and highly recommend the named arguments approach.

So, is this poor function design?

In terms of the original ReadLines() function at the top, not at all!

Function design – as with most aspects of software development – is subjective. Most would consider a function with 5 arguments as badly designed, others 10; but virtually everyone would consider a function with 20 arguments as badly designed! I’ll revisit this in a later post.

But calling a function with arguments that mean nothing to the reader (numbers and booleans especially), without using named arguments or an object? That can be considered poor programming that doesn’t help at all with debugging and maintenance.

A good guideline would be to always use named arguments, except where there’s no mistaking the purpose of an argument – e.g. File.Delete(“…”) – or where well-named variables are passed in as arguments – e.g. ReadLines(pathname, …).

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s