FREE BOOK

Chapter 8: C# 4.0 Features

Posted by Addison Wesley Free Book | C# Language February 02, 2010
This chapter looks at the new features added into C# 4.0 that combine to improve code readability and extend your ability to leverage LINQ to Object queries over dynamic data sources.

Dynamic Typing

The wow feature of C# 4.0 is the addition of dynamic typing. Dynamic languages such as Python and Ruby have major followings and have formed a reputation of being super-productive languages for building certain types of applications.

The main difference between these languages and C# or VB.NET is the type system and specifically when (and how) member names and method names are resolved. C# and VB.NET require (or required, as you will see) that static types be available during compile time and will fail if a member name or method name does not exist. This static typing allows for very rigorous error checking during compile time and generally improved performant code because the compiler can make targeted optimizations based on exact member name and method name resolution. Dynamic-typed languages on the other hand enable the member and method lookups to be carried out at runtime, rather than compile time. Why is this good? The main identifiable reason is that this allows code to locate members and methods dynamically at runtime and handle additions and enhancements without requiring a recompile of one system or another.

I'm going to stay out of the religious debate as to which is better. I believe there are positives and negatives in both approaches, and C# 4.0 allows you to make the choice depending on the coding problem you need to solve. Dynamic typing allows very clean coding patterns to be realized, as you will see in an example in this chapter coming up, where we code against the column names in a CSV file without the need for generating a backing class for every difference CSV file format that might need read.

Dynamic Typing

When an instance variable is defined with the type dynamic, the compiler ignores the call as far as traditional error checking is concerned and instead stores away the specifics of the action for the executing runtime to process at a later time (at execution time). Essentially, you can write whatever method calls (with whatever parameters), indexers, and properties you want on an instance type of a dynamic object, and the compiler won't complain. These actions are picked up at runtime and executed according to how the dynamic type's binder determines is appropriate.

A binding is the code that gets the payload for an action on a dynamic instance type at runtime and resolves it into some action. Within the C# language, there are two paths code can take at this point, depending on the binding being used (the binding is determined from the actual type of the dynamic instance):

  1. The dynamic type does not implement the IDynamicMetaObjectProvider interface. In this case, the runtime uses reflection and its traditional method lookup and overload resolution logic before immediately executing the actions.
  2. The dynamic type implements the IDynamicMetaObjectProvider interface, by either implementing this interface by hand or by inheriting the new dynamic type from the System.Dynamic.DynamicObject type supplied to make this easier.

Any traditional type of object can be declared as type dynamic. For all dynamic objects that don't implement the interface IDynamicMetaObjectProvider, the Microsoft.CSharp.RuntimeBinder is used, and reflection is employed to look up property and method invocations at runtime. The example code shown in Figure 8-1 shows the Intellisense balloon in Visual Studio 2010, which demonstrates an integer type declared as dynamic (the runtime resolves the type by the initialization expression, just like using the local type inference var keyword). No compile error occurs at design time or compile time, even though the method call is not defined anywhere in the project. When this code is executed, the runtime uses reflection in an attempt to find and execute the fictitious method ThisMethodIsNotDefinedAnywhere, which of course fails with the exception:

Microsoft.CSharp.RuntimeBinder.RuntimeBinderException: 'int' does not contain a definition for 'ThisMethodIsNotDefinedAnywhere'

If that method had been actually declared, it would have been simply invoked just like any traditional method or property call.

Figure 8-1

Any object declared as type dynamic is resolved at runtime. No errors will be reported at compile time.

The ability to have a type that doesn't implement the IDynamicObject interface should be rare. The dynamic keyword shouldn't be used in place of a proper type definition when that type is known at compile time. It also shouldn't be used in place of the var keyword when working with anonymous types, as that type is known at compile time. The dynamic keyword should only be used to declare IDynamicMetaObjectProvider implementers and for interoperating with other dynamic languages and for COM-Interop.

When to Use var and Dynamic Type Declarations

It might seem confusing as to which type definition should be used based on circumstance. Here are my recommendations:

Concrete type-Use whenever you know the type at coding time. If you know the type when defining a field, property, or return type-use it!

The var keyword-Use this when declaring an anonymous type or when capturing the result of a LINQ query projecting to an anonymous type.

The dynamic keyword-Use only when declaring a dynamic type, generally meaning a type that implements the IDynamicMetaObjectProvider interface. These can be custom types or one of the run-time binders provided by Microsoft for COM-Interop that interoperate with dynamic languages like IronPython and IronRuby. Declaring types that do not implement this interface incurs the overhead of reflection at runtime.

Specific binders are written to support specific purposes. IronRuby, IronPython, and COM-Interop are just a few of the bindings available to support dynamic language behavior from within C# 4.0. However, you can write your own and consume these types in order to solve some common coding problems, as you will see shortly in an example in which text file data is exposed using a custom dynamic type and this data is used to formulate a LINQ to Objects query.

Total Pages : 11 56789

comments