Named and Optional Arguments in C# 4.0
Many interesting news is coming from PDC this week. Part of the announcements was around Visual Studio 2010 and the .NET Framework 4.0 CTP. Microsoft has made available, a Virtual PC Image preloaded with Visual Studio.
You can download the preview information here:
- Microsoft Pre-release Software Visual Studio 2010 and .NET Framework 4.0 CTP
- Visual Studio 2010 and .NET Framework 4.0 CTP Feedback
- C# Future Samples on MSDN Code Gallery
- The Future of C# with Anders Hejlsberg
One of the interesting features of C# 4.0 is for both named and optional arguments. They are often very useful together, but are quite actually two different things. Optional arguments gives us the ability to omit arguments to method invocations, whereas named arguments allows us to specify the arguments by name instead of by position. Code using the named parameters are often more readable than code relying on argument position. These features were long overdue, especially in regards to COM interop. Both F#, and now C# have this feature which gives us greater flexibility is designing APIs.
Named and Optional Arguments in F#
As I've stated before, F# has had these features for both named and option parameters. Let's look at the implementation details of each. Named arguments are simply when creating an object, specify the name before the assignment (=) operator such as the example below.
color = Color.Black)
An optional parameter can be specified during the declaration by prefixing the argument name with a question mark (?). This becomes translated to an Option<'a> type which allows for either some value or no value at all. Should the value not be specified, you can set the default argument value through the let syntax. Let's walk through an example of a simple class called TextBoxInfo.
open System.Drawing
type TextBoxInfo(?text:string, ?color:Color, ?font:Font) =
// Initialize
let text = defaultArg text ""
let color = match color with
| None -> Color.Red
| Some c -> c
let font = match font with
| None -> new Font(FontFamily.GenericSerif, 10.0f)
| Some f -> f
// Properties
member x.Text = text
member x.Color = color
member x.Font = font
Now that we've looked at the F# implementation, let's look at how the C# team handled this issue.
The Implementation in C#
First, let's look at the named parameters. Much like F#, this process is rather straight forward. Instead of using the assignment operator (=), the colon is used to denote the name and value pair. An example of this using the above code would look like this:
var t2 = new TextBoxInfo(size : 12.0f,
width : 50.0f);
These named parameters can be used not only on these constructors, but also methods, and properties with indexers. Moving onto optional arguments, a parameter is simply declared optional by giving a default value to it through the use of the assignment operator. Let's take the above example written in F# and express is C# 4.0.
{
public TextBoxInto(
string text = "",
float size = 10.0f,
{ ... }
}
But, this doesn't work at all. Why you ask? Because, we require compile-time constraints, which means that we cannot initialize the default color this way.
Instead, if we change the above code to use the default constructor of color, such as this, the error goes away.
{
public TextBoxInfo(
string text = "",
float size = 10.0f,
float width = 50.0f,
Color color = new Color())
{ ... }
How useful is this then? Not as useful as I'd hoped. Once again, F# does get this right which allows us to initialize to whatever we so please, but it's not done in the method signature, only the fact that it is an optional parameter.
Ok, so you're probably asking about overload resolution. Using this technique will affect the resolution of your given methods. There are some rules that apply for resolution which are taken from the New Features in C# 4.0 document:
- A method signature if applicable if all its parameters are either optional or have exactly one corresponding argument (by name or position) in the call which is convertible to the parameter type.
- Betterness rules on conversions are only applied for arguments that are explicitly given - ommitted optional arguments are ignored for betterness purposes.
- If two signatures are equally good, one that does not omit optional arguments is preferred.
We can take these rules to examine the following code:
MyMethod(object o)
MyMethod(string s)
MyMethod("Hi!")
Given the above rules, the method that would be called would be the MyMethod(string s) as we take the signature that does not omit the optional arguments. Now that we have the rules in place, we can be careful of how we structure our code.
Wrapping It Up
This is one of the first looks into the future of C#. Much of the features of C# 4.0 are not around functional programming techniques as they were with 3.0, but still have some improvements. The dynamic keyword is interesting, if not just for the COM interop story. The ability to call into IronPython definitely makes the polyglot in me very happy.
As the language evolves, I believe we'll find more emphasis on the surrounding frameworks for such things as concurrency and less on the language keywords themselves. We've found that over time, if the language starts adding keywords, and somehow, for some strange reason, we're wrong about the implementation, if prematurely ages the language and possibly sends it into irrelevancy. I still have a wish list, a mile long for C#, but it'll be a more interesting discussion of what part of that would more of a framework construct instead of a language one.
I encourage you to download the VPC of the Visual Studio 2010 and .NET Framework 4.0 CTP, try out the features, and give feedback!