Untitled Page

C# 4.0 New Features

Introduction

The main theme of C# 4.0 is dynamic programming.  Objects can be dynamic in the sense that their behaviour and structure may not be known at compile type, i.e., not captured by a static type. For example, the following are dynamic objects:

  • .NET types accessed through reflection.

  • COM objects accessed through IDispatch.

  • Objects with changing structure such as HTML DOM objects.

  • Objects from dynamic languages such as Python or Ruby.

The new features of C# 4.0 therefore fall into four groups:

  1. Dynamic Binding

  2. Names and Optional Parameters

  3. CoVariance and ContraVariance

  4. COM Interop

Dynamic Binding

Summary

The key point of dynamic binding is: A dynamic object (i.e., an object declared with dynamic keyword) is assumed at compile time to support any operation, and only at runtime will an error occur if the object did not support a specific operation. The following code shows a scenario where dynamic objects can be used:

// Creates a customer

public object GetCustomer()

{

   return new Customer("Yazan");

}

 

// Call GetCustomer method. GetCustomer return an object and this cannot be changed;

// it may be third party code, or code that cannot be changed because it is public

// and is likely to break other components

Customer c = GetCustomer() as Customer;

if (c != null)

{

  // use the customer object

  c.ProcessOrder();

}

The dynamic keyword tells the compiler that a variable's type can change or that it is now known until runtime. Think of it as using an object without having to cast it. The above code can be rewritten to use dynamic variables:

// Note that there is no need to cast the returned object

dynamic c = GetCustomer() as Customer;

c.ProcessOrder(); 

The following example illustrates the above points:

public void TestDynamicBinding1()

{

    try

    {

        dynamic customer = GetCustomer();

 

        // OK

        string name = customer.Name;

 

        // Error. <>f__AnonymousType0<string,int>' does not contain a definition for 'DoSomething'

        customer.DoSomething();

    }

    catch (Exception ex)

    {

        Trace.WriteLine(ex.Mezssage);

    }

 

}

 

public object GetCustomer()

{

    return new { Name = "Yazan", ID = 1 };

}

Another interesting but highly dangerous use of dynamic variables is being able to use a dynamic variable for different types of data. The following example illustrates:

public void TestDynamicBinding2()

{

    // The following is legal

    dynamic myvar = 1.0;

    myvar = "hello";

 

    // A more meaningful use of the above scenario is when having to cast from int to long.

    // This is no longer needed with a dynamic variable. For example,

    // int n = 1;   // OK

    // n = 10L;     // Error: Cannot implicitly convert type 'long' to 'int'.

    //              // An explicit conversion exists (are you missing a cast?)

    dynamic n = 1;  // OK

    n = 10L;        // OK

}

Note that switching from dynamic to static and static to dynamic is easy:

public class Person

{

    public string Name { get; set; }

}

 

public class Employee : Person

{

    public string ID { get; set; }

}

 

public class Manager

{

    public string Title { get; set; }

}

 

 

public void TestDynamicBinding3()

{

    try

    {

        Employee emp1 = new Employee();

        dynamic  dynEmp = emp1;           // OK. static to dynamic

        Employee emp2 = dynEmp;           // OK. dynamic to static. Works because dynEmp is of type Employee

        Person   prsn = dynEmp;           // OK. dynamic to static. Works because dynEmp is of type Employee

                                          // which dervies from Person

        Manager mgr = dynEmp;             // RuntimeBinderException: "Cannot implicitly convert type

                                          //'CS4Features.Employee' to 'CS4Features.Manager'"

    }

    catch (Exception ex)

    {

        Trace.WriteLine(ex.Message);

    }

}

With a dynamic type, not only method calls can be used, but also field and property accesses, indexers and operator calls and even delegate invocations and constructors can be dispatched dynamically:

dynamic d = GetDynamicObject(...);

d.M(7);              // calling methods

d.M(x: "Hello");     // passing arguments by name

d.f = d.P;           // getting and setting fields and properties

d["one"] = d["two"]; // getting and setting through indexers

int i = d + 3;       // calling operators

string s = d(5,7);   // invoking as a delegate

C c = new C(d);      // selecting constructors 

The role of the C# compiler is simply to package up the necessary information about what is being done to d, so that the runtime can pick it up and determine what is the exact meaning of it, given an actual object referenced by d. Think of it as deferring part of the compiler’s job to runtime. If d implements the special interface IDynamicMetaObjectProvider (previously called IDynamicObject), then d is considered a dynamic object, otherwise d is treated as a standard .NET type and the operation will be resolved using .NET reflection on its type.

Static Binding vs. Dynamic Binding

Binding is the process of resolving types, members, and operation. Dynamic binding defers binding from compile time to runtime. Dynamic binding is useful when at compile time you know that a certain member, or function, or operation exists but the compiler does not. This commonly occurs when you interoperate with dynamic languages (IronPython for example) or when you use reflection. A dynamic type is declared with the keyword dynamic:

dynamic d = GetSomeObject(...);

d.SomeMethod();

In this code we expect the runtime type of d to have a method named SomeMethod. Since d is dynamic, the compiler defers binding SomeMethod to d until runtime. To understand what this means, requires distinguishing between static binding and dynamic binding.

Assume the static type of d is Person:

Person d = ...

d.GetManager();

The compiler does the binding by looking for a parameterless method named GetManager on class Person. Failing that, the compiler extends its search to methods taking optional parameters, methods on bases classes of Person, and extension methods that take Person as its first parameter. If not match is found, you'll get a compilation error. The bottom line is that the binding is done by the compiler and the binding completely depends on statically knowing the types of the operands (d in this case). This is static binding.

Now change the static type of d to object:

object d = ...

d.GetManager();

Calling GetManager gives a compilation error, because even though the value stored in d can contain a method named GetManager, the compiler cannot know since the only information it has is the type of the variable, which in this case is object.

Now change the static type of d to dynamic:

dynamic d = ...

d.GetManager();

A dynamic object binds at runtime based on its runtime type, not its compile-time type. When the compiler sees a dynamically bound expression, it merely packages the expressions such that the binding can be done at runtime. Structurally there is no difference between a dynamic object and an object reference. A dynamic reference simply enables dynamic operations on the object it points. You can convert from object to dynamic to perform any dynamic operation you want on the object.

object o = new StringBuilder();
dynamic dyn = 0;
dyn.Append("Hello");
Trace.WriteLine(o);

At runtime, if a dynamic object implements IDynamicMetaObjectProvider, that interface is used to perform the binding. If not, binding occurs as if the compiler had known the dynamic object's runtime type. These two alternatives are called custom binding and language binding, and are discussed below.

Custom Binding

Custom binding occurs when a dynamic object implements IDynamicMetaObjectProvier (IDMOP). You can implement IDMOP on types that you write in C#, however the common case is that you have an IDMOP object from al dynamic language that is implemented in .NET on the DLR (i.e., IronPython or IRonRuby). Objects from these languages implicitly implement IDMOP as a means to control the operation of these objects.

Consider the following example which illustrates custom binding:

// Class Car does not have StartEngine and StopEngine methods. Instead it uses custom
// binding to intercept and interpret all method calls
class Car : DynamicObject
{
    public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
    {
        Trace.WriteLine( binder.Name + " method was called");
 
        // Add switch or if to call logic depending on binde.Name and assign results
 
        // Assign dummy results
        result = null;
        return true;
    }
}
public static void TestDynamicCarObject()
{
    dynamic d = new Car();
    d.StartEngine();
    d.StopEngine();
}
Language Binding

Language binding occurs when a dynamic object does not support IDynamicMetaObjectProvider, Language binding is useful when working around improperly-designed types. For example, a typical problem when using numeric types is that they have no common interface. The following method works across all numeric types, so you don' have to duplicate code for all numeric types, but sacrifices static type safety risking runtime exceptions rather than compile-time errors:

// Called as follows:
//	Mean(3, 4);
//       Mean(5.0, 6.0);
// etc.
public static dynamic Mean(dynamic x, dynamic y)
{
    return (x + y)/2;
}

Dynamic binding circumvents static type safety but not runtime type safety. Also note that you cannot circumvent member accessibility rules using dynamic binding. By design, language runtime binding behaves very similarly to static binding,

RuntimeBinderException

If a member fails to bind, a RunttimeBinderException is thrown. Think of it as a compile-time error at runtime.

Runtime Representation of Dynamic

There is a deep equivalence between the dynamic and object types. Consider the following code:

public static void TestRuntimeRepresentationOfDynamic()
{
    // The runtime treats the following expressions as true
    //var isTrue1 = typeof(dynamic) == typeof(object);
    var isTrue1 = typeof (List<dynamic>) == typeof (List<object>);  // constructed types. True
    var isTrue2 = typeof(dynamic[]) == typeof(object[]);            // array types. True
 
    // Like an object reference, a dynamic reference can point to an object of any type
    dynamic d = "Hello";
    var name = d.GetType().Name;  // String
    d = 321;                      // No error even though we're using the same variable
    name = d.GetType().Name;      // Int32
 
    // Structurally there is no difference between an object reference and a dynamic reference
    // A dynamic reference simply enables dynamic operations on the object it points to. You
    // can convert from object to dynmaic to perform any dynamic operation you want on an object
    object o = new StringBuilder();
    dynamic dyn = o;
    dyn.Append("Hello");
    Trace.WriteLine(o);
}

Reflecting on a type exposing public dynamic members shows that those members are represented as annotated objects. For example, class Test is equivalent to class Test2

class Test
{
    public dynamic Name;
}
class Test2
{
    [DynamicAttribute]
    public object Name;
}

This allows consumers of class Test to know that Name is dynamic, while allowing languages that don't support dynamic binding to fall back to object.

Dynamic Conversions

public static void TestDynamicConversion()
{
    // dynamic type has implicit conversion to and from all other types
    int i = 10;
    dynamic d = i;
 
    // In the following, the runtime type of the dynamic object d must be implicitly
    // convetible to the target static dynamic
    int j = d;     // Implicit convesion from runtime type int to static type int
    long k = d;    // Implicit convesion from runtime type int to static type long
    short m = d;   // NO implicit convesion from runtime type int to static type short
                    // RuntimeBinderException: "Cannot implicitly convert type 'int' to 
                    // 'short'. An explicit conversion exists (are you missing a cast?)"
}   

var Versus dynamic

var and dynamic types may look similar but the differences are big:

  • var says: "Let the compiler figure out the type".

  • dynamic says: "Let the runtime figure out the type".

public static void TestVarVsDynamic()
{
    dynamic d = "hello";    // Static type is dynamic. Runtime time is string
    var v = "hello";        // Static type is string. Runtime time is string
    int i = d;              // Runtime error: RuntimeBinderException 
                            // Cannot implicitly convert type 'string' to 'int
    int j = v;              // Compile-time error: Cannot implicitly convert type
                            // 'string' to 'int'
    var v1 = d;             // Static type is dynamic. Runtime time is string
}

Dynamic Exceptions

The following can all be called dynamically: fields, properties, events, constructors, methods, indexers, operators and conversions.

public static void TestDynamicExpressions()
{
    // Expressions involving dynamic operands generally return dynamic types
    dynamic x = 10;
    var result = x + 20;    // static type of result is dynamic
 
    var result2 = (int)x + 10;  // static type of result2 is int since 
                                // we're casting dynamic x to int
 
    dynamic capacity = 10;
    var sb = new StringBuilder(capacity);   // constructor invocations always
                                            // yield static expressions. sb is
                                            // statically types to StringBuilder
}

Dynamic Calls Without Dynamic Receivers

When you call a method on a dynamic object, the dynamic object is considered the receiver of a dynamic function call. However, you can also statically call known functions with dynamic arguments. Such calls are subject to dynamic overload resolution. In this example, the particular Foo that gets dynamically bound depends on the run-time type of the dynamic argument:

static void TestFoo(int n) {Trace.WriteLine(n);}
static void TestFoo(string s) {Trace.WriteLine(s);}
static public void TestDynamicCallsWithDynamicReceivers()
{
    dynamic n = 10;
    TestFoo(n);            // 10
 
    dynamic s = "Hello";    // "Hello"
    TestFoo(s);
}

Static Types in Dynamic Expressions

Dynamic types are used in dynamic binding. Static types are also used - wherever possible in dynamic binding too. Consider the follows:

static void MyFoo(object x, object y) {Trace.WriteLine("object, object");}
static void MyFoo(object x, string y) { Trace.WriteLine("object, string"); }
static void MyFoo(string x, object y) { Trace.WriteLine("string, object"); }
static void MyFoo(string x, string y) { Trace.WriteLine("string, string"); }
 
static public void TestStaticTypesInDynamicExpressions()
{
    object o = "Hello";
    dynamic d = "Goodbye";
 
    MyFoo(o, d);        // object, string
    MyFoo(d, o);        // string, object
}

Uncallable Functions

The following functions cannot be called dynamically:

  • Extension methods (via extension method syntax).

  • Any member of an interface.

  • Base members hidden by a subclass.

Dynamic binding requires two pieces of information: the name of the function to call, and the object upon which to call the function. However, in each of the three uncallable scenarios above, an additional type is involved, which is only known at compile time. As of C# 4.0, there is no way to specify these additional types dynamically.:

  • Extension methods: The additional type is the static class on which the extension method is defined. The compiler searches for it using the using directive, which makes extension methods compile-time only concepts.

  • Interface members: when calling members of an interface, you specify that additional type via an implicit or explicit cast (this happens in two scenarios: 1) calling explicitly implemented interface members, and 2) calling interface members implemented in a type internal to another assembly).

Named and Optional Parameters

Named and optional parameters are really two distinct features, but are often useful together. Optional parameters allow you to omit arguments to member invocations, whereas named arguments is a way to provide an argument using the name of the corresponding parameter instead of relying on its position in the parameter list.

Some APIs, most notably COM interfaces such as the Office automation APIs, are written specifically with named and optional parameters in mind. Up until now it has been very painful to call into these APIs from C#, with sometimes as many as thirty arguments having to be explicitly passed, most of which have reasonable default values and could be omitted. Prior to C# 4.0, optional parameters were often implemented using overloaded functions:

// Method specifies all required parameters

private void Run(string name, bool bLog, List<int> lstID)

{

    // Implementation comes here

}

 

// Method specifies only one parameters. Delegates to the first method passing

// in default values for the missing parameters

private void Run(string name)

{

    Run(name, false, null);     // Pass default values for second & third parameters

}

 

// Method specifies two parameters only. Delegates to the first method passing

// in default values for the missing parameters

private void Run(string name, bool bLog)

{

    Run(name, bLog, null);      // Pass default value for third parameter

}

The above can now be written using optional parameters. A parameter is declared optional simply by providing a default value for it. In the following, bLog and lstID are optional and can be omitted in calls:

private void RunWithOptionalParameters(string name, bool bLog = false, List<int> lstID = null)

{

    // Implementation comes here

}

 

public void TestOptionalParameters()

{

    RunWithOptionalParameters("Yazan");         // false and null for second and third parameters, respectively

    RunWithOptionalParameters("Yazan", true);   // null for third parameter

    RunWithOptionalParameters("Yazan", true, new List<int>());

}

But what if you had a method with 20 optional parameters and you only wanted to specify a value for the last parameter? This is where named parameters come handy.

RunWithOptionalParameters("Yazan", new List<int>());            // Error: Argument 2: cannot convert from

                                                                // 'System.Collections.Generic.List<int>' to 'bool'

RunWithOptionalParameters("Yazan", lstID: new List<int>());     // OK.

RunWithOptionalParameters(lstID: new List<int>(), name: "Yazan"); // OK. 

Note the following extra points:

  • Optional and named arguments can be used not only with methods but also with indexers and constructors.

  • A required parameter (one that does not have a default value) cannot appear after an optional parameter.

  • A ref or out parameter cannot have a default-argument.

  • It is an error for a positional argument (argument without an argument name) to appear after a named argument.

Covariance and ContraVariance

 

Consider the following code:

 

public class Person

{

    public string Name { get; set; }

}

 

public class Employee : Person

{

    public string ID { get; set; }

}

 

void DoSomething(Person p)

{

    // Implementation ....

}

 

// Call DoSomething using instances of a base class and a derived class

Person   p = new Person();

Employee e = new Employee();

DoSomething(p);

DoSomething(e);

 

DoSomething accepts an instance of Person class or an instance of a Person-derived class (remember that public inheritance means IS A). But now suppose that DoSomething has the following signature:

void DoSomething(IEnumerable<Person> coll)

{

    // Implementation ....

}

By the same logic, when you call this function, you don't have to pass something typed as IEnumerable. The C# compiler knows it's okay to pass anything that derives from (implements) IEnumerable - say, List.  But what about passing an IEnumerabl<Employee> ? After all, if DoSomething can process a sequence of Person objects, it can surely deal with a sequence of Employee objects! Prior to C# 4.0, the compiler would throw compiler errors and did not support that scenario, i.e., passing IEnumerabl<Employee>  when it expected IEnumerabl<Person>.

 

In summary, the C# compiler understands that it's okay to vary the type of an argument, but doesn't understand that it's okay to vary the type of a generic type parameter. And generic variance just means fixing that: fixing the C# compiler so that it is okay to pass an IEnumerabl<Employee>  to a function that expects an IEnumerabl<Person>.

 

When you vary the type of an argument, you can only vary it in the direction of more derived. Passing an Employee to a function that expects a Person is okay. Passing an Object to a function that expects a Person is not okay. When you vary the type of the generic type parameter to IEnumerable, the same rule applies. A function that can deal with a sequence of Person objects can deal with a sequence of Employee objects but not with a sequence of arbitrary objects.

 

In other cases, as shown below, it turns out that the rule has to be the other way round: you can only vary the type parameter in the direction of less derived. Consider this function which takes an IComparer<T> instead of IEnumerable<T>:

void DoSomething(IComparer<Person> coll)

{

    // Implementation ....

}

If we use the IEnumerable rule and call this function with an IComparer, we have a problem. The function expects to be able to use the IComparer to compare arbitrary Person objects. If it decides to compare a Manager and a Director (both derive from Person just like Employee), our IComparer<Person> will be fail. We can't use derived types after all.

 

On the other hand, suppose we call this function with an IComparer<object>IComparer<object> can compare arbitrary objects, so it can easily cope with the specific requirement of comparing transforms. So we can pass a base type in place of the expected type.

 

So sometimes the rule is that we can only vary in the direction of more derived, and sometimes the rule is that we can only vary in the direction of less derived (more base). How do we know which rule applies in any given case? The answer is that it depends on whether the generic type parameter appears in output or input positions.

 

In IEnumerable, Person appears in an output position - it appears in the return value of GetEnumerator. Now if Person appears in an output position, then any user of the generic type, such as the DoSomething method, expects to be receiving Persons, and knows how to deal with them. So it can certainly deal with derived types such as Employee.

 

In IComparer, Person appears in input positions - it appears as the inputs to the Compare method. Now if Person appears in an input position, then a user of the generic type, such as the Compare method, is going to give us Persons, and expect us to deal with them. So we need to be able to deal with Person s at least, but if we can deal with more things, i.e. a base type, then that's not going to do any harm. To illustrate the point further, consider another example (taken from C# specs document):

List<string> lstStrings = new List<string>();

List<object> lstObjects = lstStrings;

The second assignment generates the following error:

 

Cannot implicitly convert type 'List<string>' to 'List<object>'

 

The second assignment is disallowed because a list of strings is not a list of objects.  The second statement attempted to make lstObjects and lstStrings point to the same list. If that was to succeed, then you could have written:

// If lstObjects and lstString pointed to the same list, then we could insert

// a Customer object and retrieve it as a string! This is a breach of type safety!

lstObjects[0] = new Customer();

string s = lstStrings[0];

The problem generated by the second assignment can be overcome by using IEnumerable<T> interface which has no methods to insert objects into the collection. In fact the only method exposed by IEnumerable<T> is GetEnumerator():

IEnumerable<object> lstObjects = lstStrings;    // OK.

Co- and contra-variance are about allowing assignments such as this in cases where it is safe. So the rule is: if a type parameter appears only in an output position, you can vary it in the more derived direction, and if a type parameter appears only in an input position, you can vary it in the less derived (base type) direction. C# specification refers to variance annotations which are in and out which are only used on type parameters when used with interfaces and delegate types. If the variance annotation is out, the type parameter is said to be covariant. If the variance annotation is in, the type parameter is said to be contravariant. If there is no variance annotation, the type parameter is said to be invariant. For example:

// X is covariant, Y is contra-variant, and Z is invariant

interface ISomething<out X, in Y, Z>

{

    X Foo(Y y);

    Z Bar();

}

CoVariance

 

In .NET 4.0, note the new declarations for IEnumerable<T> and  IEnumerator<T>:

public interface IEnumerable<out T> : IEnumerable

{

    IEnumerator<T> GetEnumerator();

}

public interface IEnumerator<out T> : IDisposable, IEnumerator

{

    T Current { get; }

}

The use of out means that the interfaces become covariant in T, which means that IEnumerable<A> is implicitly reference-convertible to IEnumerable<B> if A has an implicit reference conversion to B. For example, if class Employee derives from class Person, then a sequence of Employees is also a sequence of Persons:

List<Employee> lstEmployee = ...

IEnumerable<Person> lstPersons = ...

 var result = lstEmployee.Union(lstPersons);        // OK.

ContraVariance

In .NET 4.0, note the new declarations for IComparable<T>:

public interface IComparer<in T>

{

    int Compare(T x, T y);

}

The result is that an IComparer<object> can in fact be considered an IComparer<string>. This may be surprising at first, but in fact makes perfect sense: If a comparer can compare any two objects, it can certainly also compare two strings. The interface is said to be “contravariant”.

 

Limitations

Co- and contra-variant type parameters can only be declared on interfaces and delegate types. Co- and contra-variance only applies when there is a reference (or identity) conversion between the type arguments. For instance, an IEnumerable<int> is not an IEnumerable<object> because the conversion from int to object is a boxing conversion and not a reference conversion. User-defined conversions, boxing conversions, unboxing conversions, and so on, cannot be used in variance.

 

COM Interop

 

Consider the following pre- C# 4.0 code:

using Microsoft.Office.Interop;

using Microsoft.Office.Interop.Word;

 

object foo = "MyFile.txt";

object bar = Missing.Value;

object optional = Missing.Value;

 

Document doc = (Document)Application.GetDocument(ref foo, ref bar, ref optional);

doc.CheckSpelling(ref optional, ref optional, ref optional, ref optional);

There are a few problems with the code above. First, you have to declare all your variables as objects and pass them with the ref keyword. Second, you can't omit parameters and must also pass the Missing.Value even if you are not using the parameter. And third, behind the scenes, you are using huge (in file size) interop assemblies just to make one method call. C# 4.0 will allow you to write the code above in a much simpler form that ends up looking almost exactly like 'normal' C# code. This is accomplished by using some of the features already discussed; namely dynamic support and optional parameters.

using Microsoft.Office.Interop.Word;

 

dynamic doc = Application.GetDocument("MyFile.txt");

doc.CheckSpelling();