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:
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.
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 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 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,
If a member fails to bind, a RunttimeBinderException is thrown. Think of it as a compile-time error at runtime.
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.
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 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 }
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 }
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); }
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 }
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 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.
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
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>
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
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
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
In IComparer
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();
}
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.
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>
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
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();