When you think ASP, think...
Recent Articles
All Articles
ASP.NET Articles
ASPFAQs.com
Message Board
Related Web Technologies
User Tips!
Coding Tips
Search

Sections:
Book Reviews
Sample Chapters
Commonly Asked Message Board Questions
JavaScript Tutorials
MSDN Communities Hub
Official Docs
Security
Stump the SQL Guru!
Web Hosts
XML
Information:
Advertise
Feedback
Author an Article
Jobs

ASP ASP.NET ASP FAQs Message Board Feedback ASP Jobs
 
Print this Page!
Published: Wednesday, April 28, 2010

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!

- continued -

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
{
   public void Send(string from, string recipients, string subject, string body, string ccRecipients, string bccRecipients)
   {
      ...
   }
}

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
{
   public void Send(string from, string recipients, string subject, string body)
   {
      Send(from, recipients, subject, body, null, null);
   }

   public void Send(string from, string recipients, string subject, string body, string ccRecipients)
   {
      Send(from, recipients, subject, body, ccRecipients, null);
   }

   public void Send(string from, string recipients, string subject, string body, string ccRecipients, string bccRecipients)
   {
      ...
   }
}

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
{
   public void Send(string from, string recipients, string subject, string body, string ccRecipients = null, string bccRecipients = null)
   {
      ...
   }
}

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
Send("scott@example.com", "alice@example.com", "Hello!", "Optional arguments are neat!");

// Sends an email message to Alice from Scott CCing Jisun; no one is BCCed
Send("scott@example.com", "alice@example.com", "Hello!", "Optional arguments are neat!", "jisun@example.com");

// Sends an email message to Alice from Scott CCing Jisun and BCCing Sam
Send("scott@example.com", "alice@example.com", "Hello!", "Optional arguments are neat!", "jisun@example.com", "sam@example.com");

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
Send("scott@example.com", "alice@example.com", "Hello!", "Optional arguments are neat!", , "sam@example.com");

// Legal. A value is supplied for both arguments
Send("scott@example.com", "alice@example.com", "Hello!", "Optional arguments are neat!", null, "sam@example.com");

// Legal. You can supply a value for ccRecipients and not one for bccRecipients
Send("scott@example.com", "alice@example.com", "Hello!", "Optional arguments are neat!", "jisun@example.com");

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(
     from: "scott@example.com",
     recipients: "alice@example.com",
     subject: "Hello!",
     body: "Optional arguments are neat!",
     ccRecipients: "jisun@example.com",
     bccRecipients: "sam@example.com"
);

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(
     recipients: "alice@example.com",
     from: "scott@example.com",
     ccRecipients: "jisun@example.com",
     subject: "Hello!",
     body: "Optional arguments are neat!"
);

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)
Send("scott@example.com", "alice@example.com", subject: "Hello!", "Optional arguments are neat!");

// Legal. Named arguments may come after positional arguments
Send("scott@example.com", "alice@example.com", subject: "Hello!", body: "Optional arguments are neat!");

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!

  • By Scott Mitchell


    Further Readings:


  • Named and Optional Arguments (C# Programming Guide)
  • Optional Parameters and Named Arguments in C# 4
  • Visual Basic 2010 Language Enhancements
  • My Favorite New Features in Visual Studio 2010


  • ASP.NET [1.x] [2.0] | ASPMessageboard.com | ASPFAQs.com | Advertise | Feedback | Author an Article