FREE BOOK

Chapter 8: C# 4.0 Features

Posted by Addison Wesley Free Book | C# Language February 02, 2010
This chapter looks at the new features added into C# 4.0 that combine to improve code readability and extend your ability to leverage LINQ to Object queries over dynamic data sources.

Optional Arguments

The first new feature allows default arguments to be specified in a method signature. Callers of methods defined with default values can omit those arguments without having to define a specific overload matching that lesser argument list for convenience.

To define a default value in a method signature, you simply define a constant expression as the default value to use when omitted, similar to member initialize and constant definitions, except with some additional restrictions and rules. A simple example method definition that has one mandatory argument (p1, just like normal) and an optional argument definition (p2) takes the following form:

public void M ( int p1, int p2 = 5 );

The following invocations of method M are legal (will compile) and are functionally equivalent as far as the compiler is concerned:

M( 1, 5 );
M( 1 ); // the declared default for p2 (5) is used

The rules when defining a method signature that uses optional parameters are

  1. Required parameters (normally defined parameters) cannot appear after an optional parameter.
  2. The default specified must be a constant expression available at compile time or a value type constructor without parameters, or default(T) where T is a value type.
  3. The constant expression must be implicitly convertible by an identity or nullable conversion to the type of the parameter.
  4. Parameters with a ref or out modifier cannot be optional parameters.
  5. Parameter arrays (params) can occur after optional parameters, but these cannot have a default value assigned. If the value is omitted by the calling invocation, an empty parameter array is used in either case, achieving the same results.

Valid optional argument definitions take the following form:

public void M1(string s, int i = 1) { }
public
void M2(Point p = new Point()) { }
public
void M3(Point p = default(Point)) { }
public
void M4(int i = 1, params string[] values) { }

The following method definitions using optional arguments will not compile:

//"Optional parameters must appear after all required parameters"
public
void M1 (int i = 1, string s) {}
//"Default parameter value for 'p' must be a compile-time constant"
//(Can't use a constructor that has parameters)

public
void M2(Point p = new Point(0,0)) {}
//"Default parameter value for 'p' must be a compile-time constant"
//(Must be a value type (struct or built-in value types only))

public
void M5(StringBuilder p = new StringBuilder()) {}
//"A ref or out parameter cannot have a default value"

public
void M6(int i = 1, out string s = "") {}
//"Cannot specify a default value for a parameter array"

public
void M7(int i = 1, params string[] values = "test") {}

To understand how optional arguments reduce our code, Listing 8-1 shows a traditional overloaded method pattern and the equivalent optional parameter code.

Listing 8-1: Traditional cascaded method overloads is a common pattern to handle default values. Using optional arguments simplifies this pattern and makes the default discoverable in the method signature, rather than in the code body itself.

public class OldWay
{
   // multiple overloads call the one master
   // implementation of a method that handles all inputs
   public void DoSomething(string formatString)
   {
       // passing 0 as param1 default,
       // and true as param2 default.
       DoSomething(formatString, 0, true);
   }
   public void DoSomething(string formatString, int param1)
   {
       DoSomething(formatString, param1, true);
   }
   public void DoSomething(string formatString, bool param2)
   {
       DoSomething(formatString, 0, param2);
   }
   // the actual implementation. All variations call this
   // method to implement the methods function.
   public void DoSomething(
       string formatString,
       int param1,
       bool param2)
   {
       Console.WriteLine(
           String.Format(formatString, param1, param2));
   }
}


public
class NewWay
{
   // optional parameters have a default specified.
   // optional parameters must come after normal params.
   public void DoSomething(
       string formatString,
       int param1 = 0,
       bool param2 = true)
   {
       Console.WriteLine(
           String.Format(formatString, param1, param2));
   }
}

Total Pages : 11 12345

comments