The New Features in C#4.0

The co-evolution of Visual Basic and C# is increasingly going to bring the feature-set of those languages closer together. So, having walked us through the new language enhancements available in VB 2010, Hima Vejella now completes the picture by walking us through the new features in C#4.0.

In my previous article, I discussed the recent language enhancements which have been made to VB 10. Now, to complete the picture, I will take you on a quick tour of what new features are available in C# 4.0, such as interoperability with dynamic programming models, Optional and Named parameters, Generic Variance and improved Office programmability.

I’ll give you a very light overview now, and then we can step into each new feature in more detail. The new Dynamic Type feature in C# 4.0 allows us to use objects from dynamic languages such as Iron Python, Iron Ruby, and JScript, and offers better support for dynamically expanding objects like those in the HTML DOM. For example, properties can be added dynamically to a DOM element, so that rather than needing to do this:

… the Dynamic type allows us to do this, instead:

Just as a comparison, late binding in VB is similar in many ways to the new dynamic binding in C#. The Named and Optional parameters I mentioned a moment ago have been enjoyed by VB developers for some time, so you may well already be familiar with them, and I won’t dwell on them here. The Co- and Contra- Variance feature has been added in both VB 10.0 and Csharp 4.0 as part of .NET 4, allowing in and out keywords to be applied to generic parameters in some circumstances. Lastly, there is much better support for COM interop, which will make life easier for C# developers working with Office APIs (as an example). This is yet another example of support that Visual Basic developers have already had from some time.

1. Dynamic Lookup

C# 4.0 provides access to the Dynamic Language Runtime (DLR) using the new ‘dynamic’ keyword in .NET4, allowing dynamic languages like Ruby and Python to expose their objects to C#. Apart from consuming objects from dynamic languages, this feature also helps us in implementing customized dynamically dispatched objects. This can be done by implementing the IDynamicObject interface, which itself is usually done by inheriting from the abstract DynamicObject class and providing our own implementation and invocation; the IDynamicObject interface allows us to interoperate with the DLR and implement our own behavior!

Before we can go into what Dynamic type is and how it acts, we need to have a basic understanding about DLR and its components, so we’re going to spend a bit of time investigating this new feature.

Dynamic Language Runtime (DLR)

The DLR is the new API in .NET Framework 4 that is responsible for implementing dynamic programming, and is common runtime for the dynamic languages. The C# runtime is built on the top of the DLR in order to give provision for the dynamic typing. The below figure illustrates the block diagram of the DLR and its internal components.

1174-DLR.jpg

Fig 1: C#4.0 dynamic programming, and how DLR works.

The languages with dynamic capabilities (such as C#4.0 and VB 10.0) are built on top of the DLR which, as we can see above, has three main components at it’s core:

1. Expression Trees
2. Dynamic Dispatch
3. Call Site Caching

An Expression Tree depicts code in the form of a tree, which allows languages to be translated into a standard design on which the DLR can operate. These are the same kind of expression trees that were introduced in C# 3 LINQ, but which have now been improved to support statements. Once code is in a tree representation, the DLR can take the tree and use it to generate CLR code.

Dynamic Dispatch is the process of mapping messages to sequences of code at runtime . This is the way that the system lets the binders decide on the target method. Code is generated for dynamic invocations to the appropriate Language Binders.

Call Site Caching is used to avoid the need to call into the binder. Normally the binder returns an expression tree which the DLR compiles, but this step can be avoided if the types of the arguments are the “same”.

Speaking of the binders, these exist beneath the DLR, and are responsible for communicating with the respective environments of different technologies. For example, the Object binder allows communication with .NET Objects, the JavaScript binder allows communication with JavaScript in Silverlight, the Python and Ruby binders allow to communication with their respective languages, and the COM binder allows communication with Office / COM Objects.

All of this is wrapped up in the DLR, which C# 4.0 provides access to using the new ‘dynamic’ keyword. This permits data types to be decided dynamically at runtime, as opposed to statically at compile-time, by redirecting any calls involving a parameter of type dynamic through the DLR. Dynamic type signifies to the compiler that all operations based on that type should (unsurprisingly) be inferred dynamically, and instructs the compilerto ignore the compile time checking for this type.

The C# compiler now allows for calling a method with any name and any arguments on dynamically created object types. Consider the code below.

As d is declared as dynamic, the compiler will not generate any runtime errors for the above declaration and, although it will still engage in type checking, it will not decide the target data type of the call until runtime; so the actual object that is being dynamically referred to will be determined at runtime. As mentioned in the code annotation above, the compiler allows calling dynamic objects with any method or signature. Here is diagrammatic representation of how it works:

1174-dynamic1.jpg

Fig 2: The Dynamic keyword in action.

1174-dynamic2.jpg

Figure 3: Method invocation

Because C# is a statically typed language, the ‘dynamic’ type informs the compiler that it is working with a dynamic invocation, and so can forget about the normal compile-time checking for that type. However, this also means that illegal operations (if any) will only be detected at runtime.

The Difference between Var and Dynamic

People often get confused between var and dynamic, so I would like to you to understand exactly what is meant by each keyword.

If the ‘var’ keyword is used, the data type is still determined by the compiler at compile time. On the other hand, when the ‘dynamic’ keyword is used, the member and method lookups are determined at runtime. In addition, dynamic can also be used as the return type for methods, and the Var keyword cannot be used for procedure return calls.

In addition, the dynamic keyword will not give you trouble if it doesn’t have any associated method. On the other hand, var will never allow the application to compile if any method is used which is not associated with it.

An Example of Dynamic

In the above example, parameters a and b are dynamic, and so their runtime data types are used to resolve the method. If we are doing subtraction, multiplication and division operations, then using Dynamic type really saves time, as we do not need to create separate methods for each data type, like integer and double. However, make sure that you pass valid data type values, otherwise runtime errors will still be thrown. For example, you cannot use string data types for the division operator, as this is clearly illegal.

Although Dynamic is really just hiding the use of reflection under the hood, Dynamic produces the same code created by the compiler, and so has a advantage over reflection when you need dynamic access to objects at runtime. For example, consider the code below that is used to get Authors from a publisher, and which uses reflection to invoke the GetAuthor () method:

This can now be written as the code below, using the dynamic keyword:

The Dynamic type is also very helpful when interoperating with Office Automation API’s, which saves you from casting everything from the object. Finally, to finish off this look at the capabilities of the dynamic data type, bear in mind that it can be applied not only to method calls, but also for several other operations:

  • Field and property accesses,
  • Indexer and operator calls,
  • Delegate invocations and constructor calls.

Limitations

  • The Dynamic keyword cannot be used for the Class base type.
  • The Dynamic keyword cannot be used with the Operator type.
  • Extension methods cannot be used dynamically; Extension methods are introduced for the ability to add the assembly which contains the extension via a using clause. This is available at compile time for method resolution, but not at runtime; hence, dispatching to the extension method at runtime is not supported.
  • LINQ relies completely on extension methods to perform query expression operation, but extension methods cannot be resolved at runtime due to the lack of information in the compiled assembly. Hence, using LINQ Queries over dynamic objects is problematic.
  • Anonymous functions cannot be used as parameters, as the compiler cannot bind an anonymous function without knowing the type it is converting
  • A lambda expression cannot be passed in extension methods as an argument to a dynamic operation.
  • A dynamic object’s type is not inferred at the compile time of an operation, so if any error occurs, it will be identified only at runtime. Static or strongly typing is not maintained in the case of dynamic, and the introduction of dynamic C# opens the doors for duck typing.
  • Additionally, the result of any dynamic operation is itself of type dynamic, with the two exceptions:
    • The type of a dynamic constructor call is the constructed type; for example, the type of demo in the following declaration is Demo Class, not dynamic.

    • The type of a dynamic implicit or explicit conversion is the target type of the conversion.

2. Optional Parameters

Microsoft’s coevolution in C# and VB languages has made this feature possible now. These parameters need to be declared with a default value in the method signature, and allow for omitting arguments to member invocations. The below example describes the syntax:

The method above can be called in any of the following ways:

In the above code, it is mandatory to pass the name, but the remaining two parameters are optional. If we do not pass any of the values in those parameters, then the default values are passed.

Note: The Optional Parameters must be placed after the required parameters, or else the C# compiler will show a compile time error.

There are a few limitations to the optional parameters feature, and we’ll look at them after we’ve considered the new Names Arguments feature, as the two are very useful when deployed together..

3. Named Arguments

Named arguments are a way to provide an argument using the name of the desired parameter, instead of depending on its [the parameter’s] position in the parameter list. Now, if we want to omit the studentid parameter value in the above code, but specify the year parameter, the new named arguments feature (highlighted below) can be used. All of the following are also valid calls:

Named arguments are the best way to write self-documenting calls to your existing methods, even if they don’t use optional parameters. Not only can an argument use the name of the relevant parameter, but we can also change the position of the parameter if desired. The example below shows how to make use of optional and named arguments:

Although Visual Studio’s IntelliSense doesn’t deal very well with Dynamic, it works perfectly well for named and optional parameters.:

1174-intellisense.jpg

Fig 4: Named and Optional parameters IntelliSense.

The demonstration code above produces the output seen in Figure 5’s console window:

1174-Named-Optional.jpg

Fig 5: Named and Optional parameters

Limitations

As much as a I wish it were otherwise, Named and optional parameters do not permit arguments to be omitted between commas. A Method signature applies if all parameters are optional or have a corresponding argument identified by name or position in the method call. As such, the following calls are not accepted by the C-sharp Complier, because the omitted optional arguments are being ignored:

If the code needs to be consumed by expressions, optional arguments may not be permitted, because expression trees cannot contain a call or invocation that uses optional arguments. Read this post for details to understand these limitations in better.

Ultimately, the reason for introducing optional parameters to C# 4.0 is in order to better support COM interoperability. Nevertheless, this is a very good feature that allows us to omit parameters whenever they are not required, or send parameters with default values. This feature greatly helps to avoid need for multiple overloads on a constructor. However, beware of the limitations mentioned and how they work in order to use this feature to the best of its possibilities.

4. Generic Variance

The term “variance” refers to the ability to use one type where another was specified, and in that context there are 3 terms we need to become familiar with:

Invariant: A return parameter is invariant if we must use the exact match of the type name between the runtime type and declared type. For invariant parameters, neither covariance nor contravariance is permitted

Covariant: A parameter is covariant if we can use a derived type as a substitute for the parameter type, and a derived class instance can be used where a parent class instance was expected. Covariance is the conversion of a type from more specific to more general. For example, converting an object of type car to the type automobile.

Contravariant: Contravariance is exactly the opposite of Covariance, i.e. it is the conversion of a type from more general to more specific. A return value is contravariant if we can assign the return type to a variable of a less derived type than the parameter. A base class instance can be used where a subclass instance was expected.

  • Variance is a property of operators that act on types. It is the concept of specifying in and out parameters on generic types and allowing assignments where it is safe.
  • Variant type parameters can be declared for interfaces and delegate types.

Generic parameters in interfaces are invariant by default., so we need to explicitly specify whether we need a particular generic parameter to be covariant or contravariant. The example below demonstrates both co-variance and contra-variance support in C# 4.0:

Covariant parameters will only be used in output positions: method return values, get-only properties or indexers, and Contravariant parameters will only occur in input positions: method parameters, set-only properties or indexers.

Additionally, the generic variance feature also allows the assignment of the object type IEnumerable<string> to a variable with the type IEnumerable<object>.

Limitations

  • Due to a limitation in the CLR4, variant type parameters can only be declared on interfaces and delegates.
  • V ariance only applies when there is a reference conversion. Eric Lippet’s blog can be referred for more syntax options and to play with variance in depth.

5. COM Interoperability

The above features, like Dynamic type, Named and optional arguments, all undoubtedly improve the experience of interoperating with COM APIs, such as Office Automation and PIAs. There are also interesting enhancements in C#4.0 related specifically to COM Interop development, which greatly enhances productivity.

Compiling without PIAs

Primary Interop Assemblies are huge .NET assemblies generated from COM interfaces to assist strongly typed interoperability. They provide excellent support at design time, where we find the experience of the Interop is the same as if the types were really defined in .NET. However, at runtime these large assemblies can easily cause trouble for the program. We may also get versioning issues, because these assemblies are distributed independently in the application.

C#4.0’s embedded-PIA feature allows the use of PIAs at design time without having them around at runtime; the C# compiler will pull the small part of the PIA that a program actually uses directly into its assembly. So, in reality, the PIA does not have to be loaded at runtime, and there’s no need to deploy the PIAs; COM component developers only require them to build with.

Omitting References

Most of the COM APIs contains a lot of reference parameters because they need to support different programming models. In order to change a passed-in argument to pass these parameters by reference, it is now no longer required to create temporary variables for them. For COM methods, the compiler now:

  • Allows the declaration of the method call by passing the arguments by value,
  • Automatically generates the necessary temporary variables to hold the values, in order to pass them by reference,
  • Will discard these values after the call returns.

Consider this method call in C#3.0:

In C# 4.0, it can now be written as follows:

Moreover, because all the parameters that are receiving Missing.Value have default values, the declaration of the method call can even be reduced to this:

From the point of view of the programmer, the arguments are being passed by value.

Dynamic Import

In C# 3.0, most of the COM methods accept and return variant types, and these methods are represented in the PIAs as objects. It might be difficult to call these methods, as we need to cast on their return types. To make the developer’s life easier, it is now possible to import the COM APIs with PIA-embedding, in such a way that variants are instead represented using the type dynamic. So, the COM signatures now have occurrences of dynamic instead of object. As a result, it is now easy to access members directly from a returned object, and assign members to a strongly typed local variable without having to cast explicitly.

Previously you might have been writing the code as below. Here we are casting explicitly to enter a value in an Excel Cell:

However, now we can directly assign cell value, without explicitly doing type casting:

In fact, the last piece of the above code can be rewritten as:

Of course, without the Dynamic type, the value returned from excel.Cells[2, 3] is of type Object, which must be cast to the Range type before its value property can be accessed. However, when producing a Runtime Callable Wrapper (RCW) assembly for a COM object, any use of VARIANT in the COM method is actually converted to dynamic (a process called dynamification). So excel.Cells[2,3] is of type dynamic, and now we don’t have to explicitly cast it to the Range type before its Value property can be accessed. As you can see, dynamification can greatly simplify code that interoperates with COM objects.

Just to make sure I’m getting the point across, here is some simple code to demonstrate. This code creates an excel work book, and adds the text ‘simple talk’ into the specified cell:

Indexed and Default Properties

Finally, since the COM interface can be accessed dynamically, C# will now allow the declaration of indexed properties. So, instead of:

we can now write:

Limitations

These new default properties of COM interface features are allowed if we access COM dynamically, but statically typed C# code will still not recognize them.

Summary

C# has evolved from Managed code and Generics, to LINQ, and now to dynamic programming. As you will have seen in my previous article, Visual Basic 2010 already allows reference parameters to be omitted, and exposes indexed properties, and PIA embedding and variance are both being introduced to VB and C# at the same time, thanks to the languages’ co-evolution. Whereas Parallel programming used to require the help of PLINQ, a .NET4 feature that makes it possible to code in multi-core processors with much greater ease has also been added in the latest iterations of both the VB and C# languages. All in all, C#4.0 is an impressive improvement to the language, opening up new avenues for C# “old hands” to explore, and bringing familiar features in for VB or dynamic language users who are C#-curious. No matter which camp you fall in, your ability to write elegant & powerful code has just been leveled-up. As I’m exploring the latest advances in .NET, my next article is going to look at ASP.NET 4 enhancements, so stay tuned.