February 12, 2010

New features in C# 4.0

The new features in C# 4.0 are introduced considering interop & many of them already in VB .Net.
The new features fall into four groups:

  1. Dynamic lookup
  2. Named & optional parameters
  3. COM specific interop features
  4. Covariant and contravariant
Dynamic lookup
Allows you to write method, operator and indexer calls, property and field accesses, and even object invocations which bypass the C# static type checking and instead gets resolved at runtime.
void Display(dynamic obj)
WriteLine(obj); // call is decided at run-time

Display(222);   // i.e. WriteLine(int)
Display("Hello"); // i.e. WriteLine(string)

Named & optional parameters

Parameters can be specified as optional by providing a default value in a member declaration. Optional arguments can be omitted & any argument can be passed by parameter name instead of position.

public void Foo(int x, int y = 3, int z = 9);

// Here y and z are optional parameters and can be omitted in calls:
Foo(1, 2, 3); // ordinary call of Foo
Foo(1, 2); // omitting z – equivalent to Foo(1, 2, 7)
Foo(1); // omitting both y and z – equivalent to Foo(1, 5, 7)

COM specific interop features

Dynamic import

Use of dynamic import - variants are represented using the type dynamic

((Excel.Range)excel.Cells[1, 1]).Value2 = "Hello"; // before C#4.0
excel.Cells[1, 1].Value = "Hello"; // C#4.0 - dynamic import

Compiling without PIAs

Primary Interop Assemblies (PIAs) are large .NET assemblies generated from COM interfaces. At runtime these large assemblies can easily swell your program, and also cause versioning issues because they are distributed independently of your application.

The no-PIA feature allows you to continue to use PIAs at design time without having them around at runtime. Instead, the C# compiler will bake the small part of the PIA that a program actually uses directly into its assembly. At runtime the PIA does not have to be loaded.

Omitting ref

The ref keyword for callers of methods is now optional when calling into methods supplied by COM interfaces.

void AddStack(ref int x);

// the invocation can now be written as

AddStack(1); // no need for "ref"

// old style
int x = 5;
AddStack(ref x);

Covariant and contravariant

Generic interfaces and delegates can have their type parameters marked as covariant or contravariant, using keywords out and in, respectively. These declarations are then respected for type conversions, both implicit and explicit, and both compile-time and run-time

Covariant Example

public interface IEnumerable<out T> : IEnumerable{
IEnumerator<T> GetEnumerator();

public interface IEnumerator<out T> : IEnumerator{
bool MoveNext();
T Current { get; }

The “out” in these declarations signifies that the T can only occur in output position in the interface

Using the declarations above you can write the following which was previously disallowed & requires some cumbersome wrapping to get the two sequences to have the same element type:

var result = strings.Union(objects); // succeeds with an IEnumerable

Contravariance Example

Type parameters can also have an “in” modifier, restricting them to occur only in input positions.

public interface IComparer<in T>{
public int Compare(T left, T right);

Therefore, any class that implements IComparer<Base> for some class Base is also considered to be compatible with IComparer<Derived> for all classes and interfaces Derived that are extended from Base. It makes it possible to write code such as:

IComparer<object> objectComparer = GetComparer();
IComparer<string> stringComparer = objectComparer;