C# Adds Optional and Named Arguments
By Scott Mitchell
Introduction
Earlier this month Microsoft released Visual Studio 2010, the .NET Framework 4.0 (which includes ASP.NET 4.0), and new versions of their core programming languages: C# 4.0 and Visual Basic 10. In designing the latest versions of C# and VB, Microsoft has worked to bring the two languages into closer parity. Certain features available in C# were missing in VB, and vice-a-versa. Last week I wrote about Visual Basic 2010's language enhancements, which include implicit line continuation, auto-implemented properties, and collection initializers - three useful features that were available in previous versions of C#. Similarly, C# 4.0 introduces new features to the C# programming language that were available in earlier versions of Visual Basic, namely optional arguments and named arguments.
Optional arguments allow developers to specify default values for one or more arguments to a method. When calling such a method, these optional arguments may be omitted, in which case their default value is used. In a nutshell, optional arguments allow for a more terse syntax for method overloading. Named arguments, on the other hand, improve readability by allowing developers to indicate the name of an argument (along with its value) when calling a method. This article examines how to use optional arguments and named arguments in C# 4.0. Read on to learn more!
Optional Arguments
Consider a class that sends an email message. This class might have a method named
Send
that accepts string arguments specifying the important
parts of the email message: the sender, the recipient(s), the subject, the body, the CC recipients, the BCC recipients, and so forth. While some of these inputs are required -
the sender, the recipient(s), the subject, and the body - others, like the CC and BCC recipients, are clearly optional. How would you design the Send
method to
accept these arguments? One approach would be to create a single method that defines all of the arguments:
public class EmailEngine
|
While this would certainly work, developers using this code would always need to supply values for the ccRecipients
and bccRecipients
arguments, even
for emails that had no CC or BCC recipients (presumably, they'd pass in a null
value).
Another approach is to use method overloading. With method overloading you can define multiple signatures for a single method. So we could define one signature that
accepts just the four required inputs; another that accepts the four required input plus the CC recipients; and a third that accepts all six inputs. The first two methods -
the ones that accept fewer than six arguments - would simply call the Send
overload that supported all six, passing in null
for those
argument values not provided.
public class EmailEngine
|
With method overloading, the developer using this class can call the appropriate overload. If she wants to send an email without CC or BCC recipients, she can use the one that accepts just four arguments. To include CC recipients, she'd use the one that accepts five arguments. Method overloading offers a great deal of flexibility, as each method overload can have entirely different arguments and can implement their logic in unique ways. However, using method overloading to simply suppress specifying additional arguments increases the volume of code needed the class.
A third option, which offers improved readability and terseness for both the class designer and the developers using it, is to use optional arguments. Using optional arguments we get the best of both worlds: the developer designing the class can define the method using just one signature (rather than having to use multiple overloads) and the developer calling the class's method can supply only those arguments of interest, much like when using method overloading. To indicate that a method's argument is optional, specify its default value in the method signature.
Returning to the Send
method example, to indicate that the ccRecipients
and bccRecipients
arguments are optional and that, if not specified,
their value should be null
, use the following sytnax:
public class EmailEngine
|
With optional arguments, the class only needs one Send
method defined. A developer using this class can call the method with or without the optional arguments.
And when writing the code to call the Send
method, Visual Studio's IntelliSense shows the optional arguments (and their default values) in brackets.
The following uses of the Send
method are all valid:
// Sends an email message to Alice from Scott; no one is CCed nor BCCed
|
There are a couple of things to keep in mind when using optional arguments. First, any optional arguments must come after the required arguments in the method signature. For example, the following method signature is invalid because a required argument comes after an optional arguments:
public void Send(string from, string recipients, string ccRecipients = null, string subject, string body)
|
Second, the default value specified for an optional arguments must be a compile-time constant, such as null
, 3
, "Hello, World", or false
.
Values like string.Empty
and DateTime.Now
are not permitted.
Finally, when calling a method with optional arguments you cannot omit an optional argument value if you specify an optional argument value to the right of it. Returning
to our example, we could not omit the ccRecipients
argument value while providing the bccRecipients
value when calling the Send
method.
// ILLEGAL! Need to provide a value for ccRecipients if you are going to provide a value for bccRecipients
|
Named Arguments
When writing the code to call a method, Visual Studio's IntelliSense helpfully shows the names and types of the various arguments. When looking back at the code hours, days, or weeks later, you might not remember the meaning of the arguments. Returning to our earlier example, when writing the code to call the
Send
method it is clear that the first string argument specifies the sender, the second the recipient(s), and so on. But when returning to the code sometime
later it's not as clear. Consider the following method call:
Send("scott@example.com", "alice@example.com", "Hello!", "Optional arguments are neat!", "jisun@example.com", "sam@example.com");
|
Who is the email being sent from? Scott, Alice, Jisun, or Sam? And who is receiving the email, who is being CCed, and who is being BCCed? If you had recently used the
Send
method (or used it quite often) you'd likely know off the top of your head, but if it had been a while since you last used this method (or if you were
reviewing someone else's code and had never used the Send
method) you might be at a loss as to who was sending the email, who was receiving it, and who was being
CCed and BCCed.
Named arguments can help clear up any confusion. Named arguments allow the developer calling the method to precede a argument's value with its name using the following syntax. (Note: the line breaks and spacing here is to enhance readability and is not required.)
Send(
|
That's much more readable, I think you'd agree. Now there would be no confusion as to the sender, recipient, CC, and BCC values. What's more, when using named arguments the
order of the named arguments does not necessarily have to match the order of the arguments as defined in the method's signature. For example, the following call to
Send
is valid:
Send(
|
Here, the bccRecipients
argument has been omitted (as it is an optional argument) and the arguments have been reordered so that the recipient(s) is specified
first, then the sender, and then the CC recipient(s), followed by the subject and body. In fact, when using named arguments you can omit any optional arguments, regardless
of their position relative to one another. That is, using named arguments you could call Send specifying a value for bccRecipients
but omitting a value for
ccRecipients
, something that is not possible when using positional arguments (due to the fact that in the method signature ccRecipients
is defined
before bccRecipients
).
Finally, note that you can mix named and positional arguments. However, in doing so the named arguments must follow all of the positional ones. The following code shows some examples of valid and invalid calls using positional and named arguments:
// ILLEGAL! Cannot include positional arguments after a named argument (subject)
|
Conclusion
Microsoft has worked hard to better align Visual Basic and C# in this latest release. A number of features that were once the sole domain of C# have been added to VB. And as we saw in this article, a number of VB features have been added to C#, namely optional and named arguments.
Happy Programming!
Further Readings: