C# 4.0 added an “optional parameters” feature. This feature allows a method to specify default values for some parameters and allows callers to omit values for those parameters. It’s always important to distinguish features of a language (e.g. C# 4.0) from features of the framework (e.g. .NET 4.0). Optional parameters in C# were exposed in C# 4.0, but were supported by the .NET framework all the way back to .NET 1.0.
How They Work
Optional parameters are strictly a convenience feature offered by various language compilers. In CIL, there is no such thing as an optional parameter; there aren’t any markers to indicate how many arguments were specified, nor are there special “empty” arguments you can pass in place of an actual value. The CIL instructions that invoke methods always specify values for all parameters. If a method accepts two arguments, it will always read those two arguments from the stack. If you push fewer than two arguments onto the stack, the method will still just read whatever two values happen to be at the top of the stack, or exhaust the stack trying to read two values, or (most likely) just fail to run as the code will fail the verification step.
The high-level code (C#/VB/etc.) can omit values for some parameters of a call, but the resulting CIL sequence does specify these values... clearly the compiler is inserting the values during compilation. So where does the compiler look for default values, and how does it determine that the parameter is optional in the first place?
As with most features that don’t actually affect the CIL, optional parameters are described entirely in the metadata. Method parameter metadata can have a flag indicating that the parameter is optional. There is also a flag indicating whether the parameter has a default value (and a corresponding entry in the Constants table containing the value). The “optional” flag indicates that a compiler may want to let code omit this parameter, and silently insert the default value for the type (null for objects, 0 for integers, etc.). The “default value” flag indicates that there is an explicit default value that should be inserted instead of the type default.
If a language compiler is willing to look for this metadata, it can populate method arguments automatically. If the compiler doesn’t support optional parameters, it can simply ignore this metadata and require all parameters to be specified explicitly in the source code. Prior to version 4.0, the C# compiler was in the latter camp. As of C# 4.0, the compiler takes this metadata into account when compiling method calls and will insert the default values where appropriate.
C# isn’t the only language in town, and some other languages (such as VB.NET) made use of optional parameters long before C#. Interestingly, in the spirit of interoperability, C# gained the ability to mark parameters as optional long before C# 4.0, but lacked the ability to consume this flag. In other words, the compiler had logic to generate optional-parameter metadata when compiling method implementations, but did not look at that metadata when compiling method calls.
C# 1.0 / 1.2
If the Optional custom attribute was applied to a parameter in a method declaration, the compiler set the “optional” flag on the parameter metadata (and did not emit the attribute entry in the metadata). The compiler did not take the “optional” flag (or the “default-value” flag) into account when compiling method calls. Thus, libraries could expose methods with optional parameters, but could not specify the default value. If called from C#, all arguments had to be specified explicitly.
If the DefaultParameterValue custom attribute was applied to a parameter in a method declaration, the compiler set the “default-value” flag on the parameter metadata and created an entry in the Constants table containing the value (and, again, did not emit the attribute as an actual attribute entry in the metadata). The compiler still ignored the presence either flag when compiling method calls. Libraries could expose methods with optional parameters and default values, but C# callers still had to specify all arguments explicitly.
New syntax was added to specify default values: parameter names can be followed by an equals sign and a value. If a default value is specified, both the “optional” and “default-value” flags are set in the parameter metadata, and the default value is stored in the Constants metadata table. The following two samples are equivalent in C# 4.0:
void DoSomething(int parameter = 123)
void DoSomething([Optional, DefaultParameterValue(123)] int parameter)
In addition, the C# 4.0 compiler examines the “optional” and “default-value” flags when compiling a method call, and will insert any optional values that haven’t been explicitly specified. C# 4.0 able to consume default parameters.
In summary, all versions of C# can make methods with optional parameters. C# 2.0 added the ability to specify default values on those parameters, and C# 4.0 added the ability to consume optional parameters.
Interestingly, even though the void Method(int parameter = 123) syntax was only added in C# 4.0, all of the previous compilers went out of their way to detect it and tell users that it’s not supported.
error CS0241: Default parameter specifiers are not permitted
Details for the error can be found on MSDN: