Caveats of C# 4.0 optional parameters


C# 4.0 includes lots of shiny new hammers for us to bang away at every new and existing development problem we face.  One of the more interesting features is the concept of named and optional parameters.  Consider this rather cryptic bit of code:

var match = Regex.IsMatch("abcde", "cd");

So which argument is the pattern, and which is the input to match against?  With named parameters, this becomes much easier to understand:

var match = Regex.IsMatch(input: "abcde", pattern: "cd");

It’s now immediately clear which method parameter is which. That’s fantastic, this feature alone saves me tons of headaches when I order parameters incorrectly to a function that accepts objects of the same type. 

Also part of C# 4.0 are optional parameters, which can save quite a bit of typing by reducing the number of overloads to a function.  However, optional parameters come with a few caveats worth noting.

###

Expressions not supported

Let’s look at this typical controller action:

public ViewResult Index(int? minSessions)
{
    minSessions = minSessions ?? 0;

    var list = from conf in _repository.Query()
               where conf.SessionCount >= minSessions
               select conf;

I have a list view of conferences, that optionally filters on the minimum number of sessions.  In my screen, the user has a drop-down to select this minimum session count filter.  Since I don’t require this in the UI, I make that parameter a nullable int.

However, I still have to deal with the user not applying the filter, so I have some code to deal with this.  I could instead try to take advantage of optional parameters:

public ViewResult Index(int minSessions = 0)
{
    var list = from conf in _repository.Query()
               where conf.SessionCount >= minSessions
               select conf;

With the optional parameter syntax, I set the default value of minSessions to 0, removing that annoying cruft in the body of the controller action.  The action becomes much more readable.  However, I immediately run into problems when I try and compile the rest of my code base, and find that this no longer compiles:

return this.RedirectToAction(c => c.Index());

I don’t get any errors from ReSharper, but when I compile, I get a compile error:

error CS0854: An expression tree may not contain a call or invocation that uses optional arguments

Well that stinks!  I use expressions all the time to reference controller actions, as it gives me a strongly-typed links and URLs, instead of loose-typed, string-based versions.  It seems like the compiler should be able to build the correct expression tree, because the normal compiler can!  It could generate a regular Expression.Constant as if I passed in the default argument value.

But it doesn’t, and I get this compiler error instead.  So just be aware, if you want your code to be consumed by expressions, you cannot use optional arguments.

Default value localization

Another caveat C# folks might not know about is that the C# version of optional parameters suffers from the same limitations of the VB version (which, by the way, has been in VB since VS 2002).  Namely, the optional parameter value is a compiler trick, where the optional parameter value is not compiled into the method called, but instead into the caller.  Here’s an example, from a unit test:

var controller = new ConferenceController(repository, mapper);

var result = controller.Index();

result.ViewData.Model.ShouldEqual(viewModel);

I call the Index action method, which includes an optional argument.  If we open the compiled code from here, we see that the optional parameter value is baked in to the call site:

image

So what does this mean for us?  If we change the value of the optional argument from 0 to 1, we have to recompile all calling code for the calling code to get the updated value!

For folks shipping assemblies for a living, this means that optional argument values don’t version well, as callers have to recompile.  When I used to work for a company whose product included a DLL, we avoided optional method arguments for just this reason.  It’s not a reason not to use optional arguments, but it’s important to understand how they work so that you don’t run into headaches later.

Teaching ASP.NET MVC Boot Camp on May 26th