Whats new in CSharp 4.0

New Features in C# 4.0

What's new in C# 4.0? Dynamic type, Optional and Named Parameters, and Invariance, Covariance and Contravariance

The "dynamic" type

Although C# was born as a strongly typed language, "dynamic" allows us to create variables that are not strongly typed - the compiler will not scream if you invoke a function on a dynamic object without declaring it first. It will not complain when you try to stuff an integer and then follow that by putting in a string. The static type "dynamic" turns off static type checking (like certain government officials who can skip airport security or as Obi-Wan would say, "these aren't the types you're looking for").

dynamic x = 10;
x = "asdf"; //compiler won't whine and it will run ok

"dynamic" is a contextual keyword, not a reserved keyword, so your old code with a variable named "dynamic" should run just fine.

dynamic x = 10;
int dynamic = 17;
Console.Out.WriteLine("dynamic = {0}", dynamic+x); //writes 27

"dynamic" is used for interfacing with weakly typed CLR languages like Ruby, DOM objects, objects built by reflection like rehydration from XML, and objects built dynamically.

This would not fly without "dynamic"

If a method really is missing, the compiler doesn't complain, but when you execute the code, you will get a runtime error - just another reason to have all your code under unit tests. You can also catch the exception and have the "method-missing" functionality of Ruby.

dynamic penguin = new ExpandoObject();
penguin.fly(); //'System.Dynamic.ExpandoObject' does not contain a definition for 'fly'

Another reason to use "dynamic" is to make code easier to read. Scott Hanselman, a national treasure, quotes Anders with this example:

object calc = GetCalculator();
Type calcType = calc.GetType();
object res = calcType.InvokeMember("Add",
BindingFlags.InvokeMethod, null,
new object[] { 10, 20 });
int sum = Convert.ToInt32(res);

With "dynamic" it's much cleaner

dynamic calc = GetCalculator();
int sum = calc.Add(10, 20);

.Net 4.0 gives us the "ExpandoObject" in the Dynamic library.

using System.Dynamic;
dynamic house = new ExpandoObject(); //creates an object to which we can add fields and methods
house.squareFootage = 2100; //creates a new field on this object
Console.Out.WriteLine("house.squareFootage = {0}", house.squareFootage);

You can add methods as well to an ExpandoObject.

dynamic employee = new System.Dynamic.ExpandoObject();
employee.Address = "84 Rainey Street, Arlen, TX";
employee.Name = "Hank Hill";
employee.ToShippingLabel = (Func<string>)(() => employee.Name + "\r\n" + employee.Address);
Console.Out.WriteLine("employee.ToLabel = {0}", employee.ToShippingLabel());

Optional and Named Parameters

Optional Parameters

You can now make method parameters optional by giving them a default:

Before optional parameters, if you had a method which almost always took a particular value you'd have to do something like this:

private static void Move(Bird bird, bool canFly)
private static void Move(Bird bird)
       Move(bird, true);

With optional parameters you can now get the same effect with

private static void Move(Bird bird, bool canFly = true)

If a value is not passed in, it is assumed to the be default.


Named Parameters

Optional parameters are great, but what if you have a dozen of them, and you only want to specify a few in the middle? Named Parameters to the rescue. Prefixing the variable name followed by a ":" clues in the compiler which variable we are passing.

private void Register(string name="Polly", string species="parrot", string color = "white") { ...}
Register(species: "dove");

Another example - let's create a test user.

static public int CreateTestUserId(string groupName = "Baseline", string ipAddress = "",
     string browser = "Chrome", string os = "mint", string language = "en-TX",
     string status = "starting", string source = "LMS50", string pid = "mypid",
     string pii = "", int test = 0, string machineName = "testMachineName",
     string restrictionType = "u", string src = "101", string machineId = "testMachineId")

Now instead of doing this

 int userId = RDatabase.GetUserID("GroupX", "127.0.0." + i, "Opera-7.0", "WinNT", "en-UK", "Started", "LMS50", "abcdef", "-1", 1, "usausdt9999", null, "101a", Respondent.GetDebugMachineId("GetUserId"));

You can do something much more compact. As an added benefit, it focuses the reader attention to the important variables being passed.

userId = RDatabase.CreateTestUserId(source: "ibm", pid: "abcdef", src: "101a");

Named parameters can also be used for documentation even when you don't have to use them, but it makes it easier for those who read the code.

 MyDatabase.TransferMoney(from: account1, to: account2, amount: x);

Now the reader doesn't have to remember which account is being taken from and which is being added to.

(caveat: You can get into trouble with default values because the compiler simply inserts them into the calling code. If your function being called is in a different assembly, and it is updated with a new value, your code will need to be recompilied.)

Invariance, Covariance and Contravariance

Type parameters in generic interfaces and generic delegates can be designated as covariant or contravariant by using "IN" and "OUT".

Covariance is substituting a more dervived object for a less dervived object. This is a basic Object Oriented concept. If a function takes an Animal, you can pass a Tiger.

Contravariance is substituting a less dervived object for a more dervived object.

This was added to C# so the collection classes will act as we expect they should. Microsoft has a decent explaination here.