Today I decided to give optional parameters in C# a try. I’ve created a unit-test helper method that allows me to create a new build definition in Team Foundation Server. The method itself accepts three parameters, two of them are optional. So I created a method signature like this:
1: public void CreateLegacyBuild(string teamProjectName,string buildDefinitionName,string buildDefinitionPath = "...")
3: // Implementation of the method
This saves you the trouble creating an extra overload to provide a value for the optional parameter. If you do this however, you will get a code analysis warning with the code: CA1026. This warning tells you not to use default parameters, but instead supply an overload for the method.
CA1026 : Microsoft.Design : Replace method ‘…’ with an overload that supplies all default arguments.
It looks like optional parameters are not so great after all. Instead of saving time it only gives FxCop more material to start arguing about. Or is there more to this warning than meets the eye?
What are optional parameters
Before I start complaining about FxCop doing a bad job, I will tell you about optional parameters and how they work. This is important, because it tells that little bit extra that you need to understand what is really going on with the code analysis warning I talked about in the previous section.
Default parameters are best explained using an example:
1: public void DoSomething(int firstParameter, int secondParameter = 10, int thirdParameter = 20)
3: //TODO: Do something interesting
As you can see for two parameters of the method there’s a default value specified in the method signature. There three ways in which you can invoke a method that has optional parameters. One way is by skipping the optional parameters altogether like this:
The second way to invoke a method with optional parameters is to supply values for the optional parameters, as if you were using the maximum overloaded version of the method.
The last way in which you can invoke a method with optional parameters is to name the optional parameters. This allows you to skip certain optional parameters while supplying a value for the ones that you need.
1: DoSomething(10, thirdParameter: 60);
What you can’t do with optional parameters is skipping them like you would in VB6. For example, the following call would be invalid in C#.
Now that you know how optional parameters work, let’s take a closer look at how the compiler is handling optional parameters.
When the compiler compiles the source file containing the method with the optional parameters, it will embed the default value as a .param instruction in the method itself. This .param instruction is NOT used during runtime, but is there as a form of metadata.
If you add a call to a method in your own code, the compiler will bake the default value for optional parameters into the MSIL code for the method call as if you were providing values for them.
The way the compiler processes optional parameters is somewhat tricky. If you change the default values for the optional parameters and recompile the code for the method, the actual default values in your code that invokes the method will not get updated.
In the previous section if shown what happens in the compiler when you use optional parameters. An important conclusion from this is that the default value for an optional parameter becomes part of the “contract” of your assembly. This is however invisible to the user. Also, if you change the default value of an optional parameter you are most likely introducing bugs into your own code.
The quotes around the contract bit are on purpose, because after changing the default values for the optional parameters change, the contract doesn’t look any different to the user. The user doesn’t get any hints telling him the default value has changed. In fact, if the user doesn’t compile his code, the implementation of your method will break.
My advice for using optional parameters: Only use them for methods that have an accessibility modifier of internal or less. For other scenarios I suggest you introduce overloads to make the API more accessible to developers.
So what’s the verdict on code analysis warning CA1026? The problem identified with this rule is real and you shouldn’t exclude it without giving your code a closer inspection. If you get this warning you should consider changing your code, because there’s nothing worse than implementations that break because of invisible changes.
One other thing that I’ve learned from this experience is this: New features are cool, but more often than not make things more complicated. We really should be really careful introducing them.
I hope this article helps getting more insight into why FxCop is a good idea and what goes behind some of the seemingly less relevant rules.