Jack @ ASP.NET

As a software engineer, I focus on .NET, especially asp.net, C#, WCF and so on, and I am also very interested in Search Engine Optimization.

Entries Tagged ‘optional parameters’

Optional Parameters in C# 4.0

Just like other languages such as vb, and c/c++, C# can support the optional parameters from 4.0.

here is a code snippet

   1:  class Program
   2:  {
   3:      static void Main(string[] args)
   4:      {
   5:          MyTest("P1", "P2");
   6:          MyTest("P1");
   7:      }
   8:   
   9:      static void MyTest(string parameter1, string parameter2="Default Value 2")
  10:      {
  11:          Console.WriteLine("Para1={0}, Para2={1}", parameter1, parameter2);
  12:      }
  13:  }

And the returning result will be

Para1=P1, Para2=P2

Para1=P1, Para2=Default Value 2

and the parameter2 will use the default value in the definition if you do not give a value. Quite convenience, isn’t it?

Versioning and optional parameters

The restrictions on default values for optional parameters may remind you of the restrictions on const fields or attribute values, and they behave very similarly. In both cases, when the compiler references the value, it copies it directly into the output. The generated IL acts exactly as if your original source code had contained the default value. This means if you ever change the default value without recompiling everything that references it, the old callers will still be using the old default value. To make this concrete, imagine this set of steps:

  1. Create a class library  with a class like this:
    public class MyDemo
    {
    	public static void PrintValue(int value = 10)
    	{
    		System.Console.WriteLine(value);
    	}
    }
  2. Create a console application (Application.exe) that references the class library:
    public class Program
    {
    	static void Main()
    	{
    		MyDemo.PrintValue();
    	}
    }
  3. Run the application—it’ll print 10, predictably.
  4. Change the declaration of PrintValue as follows, then recompile just the class library:
    public static void PrintValue(int value = 20)
  5. Rerun the application—it’ll still print 10. The value has been compiled directly into the executable.
  6. Recompile the application and rerun it—this time it’ll print 20.

This versioning issue can cause bugs that are hard to track down, because all the code looks correct. Essentially, you’re restricted to using genuine constants that should never change as default values for optional parameters. There’s one benefit of this setup: it gives the caller a guarantee that the value it knew about at compile-time is the one that’ll be used. Developers may feel more comfortable with that than with a dynamically computed value, or one that depends on the version of the library used at execution time.

Of course, this also means you can’t use any values that can’t be expressed as constants anyway—you can’t create a method with a default value of “the current time,” for example.

Optional Parameters in C# 4.0

Some members of the C# community were discussing optional parameters and the implications for future versions when optional parameter values change.  In short, you need to realize that changing the value of optional parameters in a public API is a change that is observable at client code. The ramifications vary greatly, from “no big” to “stop the world”. I’ll give a brief explanation of how the feature works, and what you need to watch for and how to separate reasonable caution from irrational fear from using a feature.

While most modern programming languages provide some way of declaring optional function parameters, C# doesn’t provide a way of directly doing so, despite the fact that VB.NET and .NET’s attribute system both support this functionality. This is a subject of some debate currently in the C# community. The C# development team’s position seems to boil down to the following: When provided, this feature is usually nothing more than dressing up method overloading with a little syntactic sugar.

When you get right down to it, their position makes some sense. A function with an optional parameter is in reality two different functions: one that assumes some default behavior if the optional parameter is omitted, and another that performs more specific behavior based on the value of the optional parameter if provided. But that doesn’t change the fact that using overloaded methods to provide optional parameter support feels a little clunky. It works, but you always wind up writing more code, and you pollute your object interface with the extra method signatures required to support all of your optional parameters. Let’s look at some alternatives.

Optional Parameters in C# 4.0

The definition of a method, constructor, indexer, or delegate can specify that its parameters are required or that they are optional. Any call must provide arguments for all required parameters, but can omit arguments for optional parameters.

Each optional parameter has a default value as part of its definition. If no argument is sent for that parameter, the default value is used. Default values must be constants.

Optional parameters are defined at the end of the parameter list, after any required parameters. If the caller provides an argument for any one of a succession of optional parameters, it must provide arguments for all preceding optional parameters. Comma-separated gaps in the argument list are not supported. For example, in the following code, instance method ExampleMethod is defined with one required and two optional parameters.

E.g.

public void ExampleMethod(int required, string optionalstr = "default string", int optionalint = 10)

The following call to ExampleMethod causes a compiler error, because an  argument is provided for the third parameter but not for the second.

//anExample.ExampleMethod(3,  ,4);

However, if you  know the name of the third parameter, you can use a named argument to  accomplish the task.

anExample.ExampleMethod(3, optionalint: 4);

IntelliSense uses brackets to  indicate optional parameters, as shown in the following illustration.
Optional parameters in ExampleMethod
Consider a standard scenario with method overloading or constructor chaining.  In C# we’d have several methods with different signatures where, in effect, we’re really just after default values. Let’s take the scenario where we’ve got a little helper class to send emails in our application. In some cases we want to CC the administrator to troubleshoot issues; in some cases we want rich HTML emails rather than plain text. We might set up our methods like this: 1:  public void SendMail(string toAddress, string bodyText) 2:  { 3:      this.SendMail(toAddress, bodyText, true); 4:  } 5:    6:  public void SendMail(string toAddress, string bodyText, bool ccAdministrator) 7:  { 8:      this.SendMail(toAddress, bodyText, ccAdministrator, false); 9:  } 10:    11:  public void SendMail(string toAddress, string bodyText, bool ccAdministrator, bool isBodyHtml) 12:  { 13:      // Full implementation here 14:  } This is pretty standard method overloading and we essentially are setting default values (true for CC the Admin, and false for HTML emails). With C# 4.0 we can now make the code more concise by only having to implement 1 method: 1:  public void SendMail(string toAddress, string bodyText, bool ccAdministrator = true, bool isBodyHtml = false) 2:  { 3:      // Full implementation here 4:  } However, you do have to take into account your scenario.  If you have a situation where you actually need to know if the consuming code provided a value then this isn’t a good option because if “true” comes in for the 3rd parameter, you don’t know if the consuming code actually set this explicitly or if it was simply the result of the default value.  But in typical scenarios like this, it’s not a big deal.  Cracking open Reflector and looking at the IL that the C# compiler is generating: 1:  .method public hidebysig instance void SendMail(string toAddress, string bodyText, [opt] bool ccAdministrator, [opt] bool isBodyHtml) cil managed 2:  { 3:      .param [3] = bool(true) 4:      .param [4] = bool(false) 5:      .maxstack 8 6:      L_0000: nop 7:      L_0001: ret 8:  } Which Reflectors translates to a C# equivalent of: 1:  public void SendMail(string toAddress, string bodyText, [Optional, DefaultParameterValue(true)] bool ccAdministrator, [Optional, DefaultParameterValue(false)] bool isBodyHtml) 2:  { 3:  }

Effective C#, What Content got dropped

It should be clear from the additional content what’s new: I added coverage of the significant C#4.0 features like dynamic invocation and named / optional parameters. New library additions like PLINQ are also covered.

It’s much harder to see how I decided which items to drop. There are 15 completely new items in the 2nd edition, so that meant finding 15 items to drop. (Several other items have the same title, but were significantly rewritten – that will be the subject of another blog post.) Here’s how I decided which items to remove:

Items that are less important now. A number of the items in the first edition discussed techniques that were much more important before generics were available. Some of these items were those that discussed boxing and unboxing, the collection classes, and the data set class. All of those techniques and libraries were far more useful in C# 1.x than in the current .NET universe.

Items that have become common practice. C# has been around for almost a decade, and the community is much more mature than it was in 2004, when Effective C# was published. Some of the items are part of the conventional wisdom now

Items that assumed your last language was C++ or Java. The early adopters of C# were developers that came from C++ (on the Windows platform) along with some developers that came from Java. That’s no longer true.  College grads (since 2002 or so) are using C# for their first professional programming experience. Others are coming from VB, Ruby, Python, or PHP. (I’m not claiming that C# is grabbing market share from all those languages; the migration happens in all directions.) It just wasn’t right to assume that every C# developer has C++ or Java experience anymore.

The poster child for dropping items is the original Item 41, where I advocated using DataSets rather than implementing IBindingList yourself. I didn’t rewrite this item because the obvious answer now is to use BindingList<T> when you need the IBindingList capability. If you were using DataSets for some other reason, pick some other generic collection type. There are many, and the options grew again in .NET 4.0. Those generic collections have better APIs (the type parameter means the compiler ensures type correctness), and better performance (boxing / unboxing doesn’t apply. It’s not often that it’s trivial to get better performance and better chances at correctness. Even in the 1.x days, I didn’t advocate using DataSets are part of a service API. That was and still is a painful choice.

There’s also been many enhancements in the .NET framework that mean there are better data solutions. LINQ, along with the query syntax pattern (See Item 36 in More Effective C#), means there are much better ways to work with data in .NET 4.0.  Chapters 4 and 5 of More Effective C# discuss these important techniques. The entity framework has matured, and is a better way to handle data transfer between layers and machine boundaries. (I still need to look more closely at the latest EF, I know some of the changes, but not all).