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.

Related Articles:

Post Footer automatically generated by Add Post Footer Plugin for wordpress.

About Jimmy Bogard

I'm a technical architect with Headspring in Austin, TX. I focus on DDD, distributed systems, and any other acronym-centric design/architecture/methodology. I created AutoMapper and am a co-author of the ASP.NET MVC in Action books.
This entry was posted in C#. Bookmark the permalink. Follow any comments here with the RSS feed for this post.
  • http://weblogs.asp.net/BrianDukes Brian Dukes

    I think your first error only applies to expressions which make use of the optional parameter, i.e. c.Index() isn’t valid in an expression, but c.Index(0) is valid.

  • Eric Sch.

    Thanks for the infos. As addition:
    Optional parameter with an expression tree in
    “return this.RedirectToAction(c => c.Index());” should still work, if you define the parameter value, ex. “return this.RedirectToAction(c => c.Index(0));”.
    But agreed, in this case the optional parameter would be useless.

  • For #2 I’d say OPs shouldn’t be used

    If it causes a recompile when its not even a relink, that the set of signatures for each class/method are identical, then it’s not worth it.

  • joe

    VB has had optional parameters since before it was a .net language. And they were frequently used as a hack to work around the lack of method overloads.

  • http://brendan.enrick.com/ Brendan Enrick

    With the RegEx example there is something else that’s been built into every version of C# which also allows us to know which one is the pattern and which one the input.

    I think the technical name is “variables”. Once we extract those literals into variables like you obviously do in your production code it becomes very obvious which parameter is which. (I admit we can’t at a glance tell if you mixed them up though. I hope you tested the code and made sure it worked though.)

  • http://www.lostechies.com/members/bogardj/default.aspx bogardj

    @Brendan

    In the original code, the input was a variable, but the pattern was a literal. Well-named variables still won’t guarantee I don’t accidentally switch the order when I call the method, however.

  • Graham

    I’ve seen some really insidious bugs caused by, admittedly bad, use of optional parameters (in VB6). Where the method signature had changed but the calling code hadn’t, meaning the arguments had shifted one to the left (or right). Like anything, you just have to be careful how you use it.

  • http://twitter.com/samuelpearson Sam

    Argh, I just got burned by CS0854. Boo urns!

  • Jerome Meyers

    You write: “However, I still have to deal with the user not applying the filter, so I have some code to deal with this.”  So… how do you get around this?  This is exactly the problem I am encountering.  I can’t use optional parameters… so, what do I do?

    Thanks,
    jeromeyers

    • Anonymous

      Nullable types are there for ya! You can do int? instead of int foo = 0

  • Pingback: My Daily Shared Bookmarksfboiton's blog | fboiton's blog

  • http://wisentechnologies.com/it-courses/.net-training.aspx .Net Training in Chennai

    C# does not
    support comma-separated gaps in the argument list.