Optional and Named Arguments
A long-requested feature for C# was to allow for method arguments to be optional. Two closely related features in C# 4.0 fulfill this role and enable us to either omit arguments that have a defined default value and/or to pass arguments by name rather than position (skipping those arguments that are optional).
The main benefit of these features is to improve COM-Interop programming (which is covered shortly) and to reduce the number of cascaded method overload chained calls. It is a common programming pattern to have a master method signature containing all arguments (containing the actual implementation) chained to a number of overloaded methods that have a lesser argument signature set calling the master method with hard-coded default values for the omitted arguments. This common coding pattern becomes unnecessary when optional arguments (and/or names arguments) are used in the definition of the aforementioned master method signature, arguably improving code readability and debugging (see Listing 8-1 for an example of the old and new ways of creating multiple overloads).
There has been fierce debate on these features on various email lists and blogs. Some C# users believe that these features are not necessary and introduce uncertainty in versioning. For example if version 2 of an assembly changes a default argument value for a particular method, client code that was assuming a specific default might break. This is true, but the existing chained method call pattern suffers the identical issue-default values are coded into a library or application somewhere, so thinking about when and how to handle these hard-coded defaults would be necessary using either the existing chained method pattern or the new optional and default argument patterns. Given that optional arguments were left out of the original C# implementation (even when the .NET Runtime had support and VB.NET utilized this feature), we must speculate that although this feature is unnecessary for general programming, coding COM-Interop libraries without this feature is unpleasant and at times infuriating-hence, optional arguments and specifying arguments by name has now made its way into the language.
COM-Interop code has always suffered due to C#'s inability to handle optional parameters as a concept. Many Microsoft Office COM libraries, like those built to automate Excel or Word for instance, have method signatures that contain 25 optional parameters. Previously you had no choice but to pass dummy arguments until you reached the "one" you wanted and then fill in the remaining arguments until you had fulfilled all 25 arguments. Optional and named parameters solve this madness, making coding against COM interfaces much easier and cleaner. The following code demonstrates the before and after syntax of a simple Excel COM-Interop code snippet and shows how the excel.Workbooks.Open method is cleaned up when using C# 4.0 (compared to C# 3.0).
// Old way – before optional parametersvar excel = new Microsoft.Office.Interop.Excel.Application();try{ Microsoft.Office.Interop.Excel.Workbook workBook = excel.Workbooks.Open(fileName, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing); // Do work with Excel workBook.Close(false, fileName);}finally{ excel.Quit();}// New Way - Using optional parametersvar excel = new Microsoft.Office.Interop.Excel.Application();try{ Microsoft.Office.Interop.Excel.Workbook workBook = excel.Workbooks.Open(fileName); // Do work with Excel workBook.Close(false, fileName);}finally{ excel.Quit();}
The addition of object initializer functionality in C# 3.0 took over some of the workload of having numerous constructor overloads by allowing public properties to be set in line with a simpler constructor (avoiding having a constructor for every Select projection needed). Optional and named arguments offer an alternative way to simplify coding a LINQ Select projection by allowing variations of a type's constructor with a lesser set of arguments. Before diving into how to use these features in LINQ queries, it is necessary to understand the syntax and limitations of these new features.